Important packages:

setwd("Dataset")
Warning: The working directory was changed to C:/Users/DELL/Documents/GitHub/Project_IT326/Dataset inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
Dataset <- read.csv("data.csv")
Preprocessed_dataset <-  read.csv("preprocessed_dataset.csv") 
if(!require(ggplot2)){
install.packages("ggplot2")}
library(ggplot2)
if(!require(dplyr)){
install.packages("dplyr")}
library(dplyr) 

if(!require(ltm)){
install.packages("ltm")}
library(ltm)
if (!require(cluster, quietly = TRUE)) {
  install.packages("cluster")
}
if (!require(factoextra, quietly = TRUE)) {
  install.packages("factoextra")
}
 
if (!require(caret, quietly = TRUE)) {
  install.packages("caret")
}
if (!require(party, quietly = TRUE)) {
  install.packages("party")
}
if (!require(partykit, quietly = TRUE)) {
  install.packages("partykit")
}

if (!require(rpart, quietly = TRUE)) {
  install.packages("rpart")
}
if (!require(rpart.plot, quietly = TRUE)) {
  install.packages("rpart.plot")
}
if (!require(tidyverse, quietly = TRUE)) {
  install.packages("tidyverse")
}

if (!require(RWeka, quietly = TRUE)) {
  install.packages("RWeka")
}

IT326 Project

_________________________________________________________________________________________________________

The Goal and problem

Our primary objective of this analysis is to classify whatever a student will go to college or not we aim to discover the most influential variables and their relationships with the decision not to attend college to provide counseling session to the student to help them

Data mining Task

using the classification methods and to identify the main factors and reasons why students are less likely to pursue higher education using class label indicated “will_go_to_college” being ‘False’. By leveraging the provided dataset with attributes such as school type, school accreditation, gender, interest in college, residence and use clustering to group pattern of the student who will go to collage or not

The Source

Kaggle.com

General information

  • Number of attributes : 11
  • Number of rows (objects) : 1000
  • The class label: The class label in our project is the attribute “will_go_to_college”. This attribute is binary, meaning that it can take on two values: True for yes or False for no. The value of this attribute will be the target variable that we are trying to predict during our project.

Attribute Description Type Possible values
type_school The type of school the student attends Binary Academic / Vocational
school_accreditation The quality if school Binary A / B (A is better than B)
gender The student’s gender Binary Male / Female
interest The student’s interest in going to college Nominal Very interested /Interested / Less Interested / Not Interested /Uncertain
residence The student’s residence Binary Urban / Rural
parent_age The age of the student’s parents Numeric 40 - 65
parent_salary

The monthly salary of the student’s parents in IDR/Rupiah.

[1Rupiah = 0.00024SAR]

Numeric 1000K - 10M
house_area The size of the student’s house in meter square Numeric 20 - 120
average_grades The student’s average grades in school. Numeric 75 - 98
parent_was_in_college Whether the student’s parents attended college. Binary True - False
will_go_to_college Whether the student plans to go to college. Binary True - False

Sample of our data

head(Dataset)

Here are a sample of 6 row from our dataset.

Missing values

sum(is.na(Dataset))
[1] 0

There are no missing values in our dataset.

Statistical graphs

Graph 1:

df =data.frame(Dataset)
ggplot(data=df, aes(x = interest, fill = will_go_to_college)) +
  geom_bar() +
  scale_x_discrete(limits = c('Not Interested', 'Less Interested', 'Uncertain', 'Interested', 'Very Interested')) +
  labs(title = 'College interest vs College attendance ') +
  scale_fill_manual(values = c("True" = "antiquewhite2", "False" = "antiquewhite3")) +
  theme_minimal()

NA
NA

According to the graph, whether students are interested in going to college or not does not affect whether they actually end up attending Collage . There is a group of individuals who were interested in attending but did not receive acceptance, while others who were not interested were accepted.

Graph 2:


filtered_True =filter(Dataset, will_go_to_college == 'True')
filtered_False =subset(Dataset, will_go_to_college =='False')

ggplot() +
  geom_density(data = filtered_True, aes(x = average_grades, fill = "Going to College"), alpha = 0.5) +
  geom_density(data = filtered_False, aes(x = average_grades, fill = "NOT Going to College"), alpha = 0.5) +
  labs(x = "Average Grades", y = "Density") +
  ggtitle("Comparison of Average Grades for Students Going to College and NOT Going to College") +
  scale_fill_manual(values = c("Going to College" = "antiquewhite4", "NOT Going to College" = "antiquewhite1"))

The graph shows that the average grades for students who had accepted to go to college were higher than those who did not enter college , and this indicates the existence of a correlation between those who going to college and the average grades

Graph 3:

Dataset_percentage <- Dataset %>%
  group_by(type_school) %>%
  summarise(percentage = mean(will_go_to_college == "True") * 100)

# Create a percentage chart
ggplot(Dataset_percentage, aes(x = type_school, y = percentage, fill = type_school)) +
  geom_bar(stat = "identity") +
  labs(title = "Percentage of Students Going to College by Type of School",
       x = "Type of School",
       y = "Percentage") +
  scale_fill_manual(values = c("Academic" = "antiquewhite3", "Vocational" = "antiquewhite2")) +
  theme_minimal()

NA
NA

This graph shows the impact of the type of high school attended by students on their college attendance . Based on the bar chart:

  • among students from Academic high schools, 313 are going to college, and 296 are not.

  • among students from Vocational high schools, 187 are going to college, and 204 are not

These information tell us that a higher proportion of students from academic high schools are going to college compared to those from vocational schools which suggest that the type of school attended.

Graph 4:

ggplot(Dataset, aes(x = average_grades)) +
  geom_histogram(binwidth = 5, fill = "antiquewhite2", color = "antiquewhite4") +
  labs(title = "Distribution of students' grades",
       x = "Students' average grades",
       y = "Frequency") +
  theme_minimal()

This histogram show us that the majority of the students in the dataset are performing well since it seems like their grades are spanning between 75 and 98. This analysis will help us determine whether the academic performance level of students is a contributing factor to their college attendance or not.

Statistical Measures

  1. The student’s academic performance analysis:
summary(Dataset$average_grades)

The student grades in our dataset range from 75.00 to 98.00, with a median of 85.58 and an average of 86.10. This suggests that most students are doing well as none of them have average grades below 50. However, it’s interesting to note that some students have much higher or lower grades than the average, mainly due to the wide range of grades..

  1. The socioeconomic status of students’ families analysis:
summary(Dataset$parent_salary)

In the dataset, we’ve got students parents with salaries ranging from 1,000,000 to 10,000,000 IDR/Rupiah. The median salary is 5,440,000 IDR/Rupiah, and the average is 5,381,570 IDR/Rupiah. This data tells us that many parents in our dataset earn less than the average salary in Indonesia, which is 146,000,000 IDR. This suggests that quite a few students in our dataset come from families with limited finances. And this financial situation could certainly impact their ability to get through college.

Additionally we can utilize the house area attribute to gain a deeper understanding of the socioeconomic status of students’ families, where students with houses significantly larger than the mean might indicate a higher socioeconomic status, while those with houses considerably smaller than the mean might reflect a comparatively lower socioeconomic status. Based on the shown output, the house areas range from [20.00-120.00 ㎡]. The median house area is 75.50 ㎡ indicates that families with house areas around this value likely have moderate socioeconomic status with houses that neither very small nor very large.

  1. Understanding Parent Age Range and Variation in its Values
summary(Dataset$parent_age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  40.00   50.00   52.00   52.21   54.00   65.00 
SD=sd(Dataset$parent_age)
MeanAge=mean(Dataset$parent_age)
cat("coefficient of variation:",SD/MeanAge*100,"%")
coefficient of variation: 6.704771 %

This summary provides the range for age attribute [40,65] which indicates that all parent in middle age during this age parent have more concern about their children , the coefficient of variation= 6.7% which indicates lower variation ,and the value of attribute parent_agerare are relatively close to the mean overall 25% of them have an age below or equal to 50 , 75% have an age below or equal to 54 and the median value is 52

Preprocessing data

we aim to increase the quality of data and transforming the data to make it suitable for analysis

Outliers analysis

###parent age outliers
quartiles <- quantile(Dataset$parent_age, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$parent_age)

Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR 

data_no_outlier <- subset(Dataset, Dataset$parent_age > Lower & Dataset$parent_age < Upper)
dim(data_no_outlier)
[1] 957  11
###parent salary outliers
quartiles <- quantile(Dataset$parent_salary, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$parent_salary)

Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR 

data_no_outlier <- subset(data_no_outlier, data_no_outlier$parent_salary> Lower & data_no_outlier$parent_salary < Upper)
dim(data_no_outlier)
[1] 955  11
###averge grades outliers
quartiles <- quantile(Dataset$average_grades, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$average_grades)

Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR 

data_no_outlier <- subset(data_no_outlier, data_no_outlier$average_grades> Lower & data_no_outlier$average_grades < Upper)
dim(data_no_outlier)
[1] 944  11
###house area outliers
quartiles <- quantile(Dataset$house_area, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$house_area)

Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR 

data_no_outlier <- subset(data_no_outlier, data_no_outlier$house_area> Lower & data_no_outlier$house_area < Upper)

Founded_Outliers=data.frame(anti_join(Dataset,data_no_outlier))
Joining with `by = join_by(type_school, school_accreditation, gender, interest, residence, parent_age, parent_salary, house_area, average_grades, parent_was_in_college, will_go_to_college)`
print(Founded_Outliers)

After conducting data analysis and identifying outliers, our inspection reveals that the detected outliers represent inherent variation within the population. Regarding Parent_age, outliers are observed for values below 44 and above 65. However, it should be noted the age from 40 to 65 fall within the expected mean of our dataset meaning that it doesn’t indicate that they are outliers . For parent_salary, we found two outliers: one below 1,326,250 ind ≈ 85 USD and another above 9,416,250 ind ≈ 606 USD. The minimum and maximum values were determined to be 1,000,000 ind ≈ 64 USD and 10,000,000 ind ≈ 644 USD, respectively. In the case of grades, twelve outliers were identified, ranging from below 76 to above 97. Nevertheless, since the data falls within the acceptable range of 0 to 100, these outliers should be retained as they are still considered normal and within the usual grade range. Finally, for house_area, we found eleven outliers below 34.4m and above 115m, with the minimum being 20m and the maximum being 120m. However, these values are still considered typical for the population.

Normalization

normalize <- function(x) {return((x-min(x))/ (max(x)-min(x)))}
datasetWithoutNormalization<-Dataset
Dataset$parent_salary<-normalize(datasetWithoutNormalization$parent_salary)
Dataset$house_area<-normalize(datasetWithoutNormalization$house_area)
print(Dataset)

We applied normalization to the ‘parent_salary’ and ‘house_area’ attributes, scaling their values to a range between 0 and 1. This normalization process greatly facilitates data handling and analysis, ensuring that these attributes are on a consistent scale. Which will improve the reliability of our data analysis and enable better conclusions to be drawn from the dataset. Normalization is a crucial step in preparing the data for modeling, as it prevents attributes with larger numerical ranges from dominating the analysis and ensures fair treatment for all features.

Discretization



Dataset$average_grades [Dataset$average_grades >= 95] <- '+A'
Dataset$average_grades [95 >Dataset$average_grades & Dataset$average_grades >= 90] <- 'A'
Dataset$average_grades [90 >Dataset$average_grades & Dataset$average_grades >= 85] <- '+B'
Dataset$average_grades [85 >Dataset$average_grades & Dataset$average_grades >= 80] <- 'B'
Dataset$average_grades [80 >Dataset$average_grades & Dataset$average_grades >= 75] <- '+C'
Dataset$average_grades [75 >Dataset$average_grades & Dataset$average_grades >= 70] <- 'C'
Dataset$average_grades [70 >Dataset$average_grades & Dataset$average_grades >= 65] <- '+D'
Dataset$average_grades [65 >Dataset$average_grades & Dataset$average_grades >= 60] <- 'D'
Dataset$average_grades [60 >Dataset$average_grades & Dataset$average_grades >= 0] <- 'F'
Dataset$average_grades <- as.character(Dataset$average_grades )
print(Dataset)

We transformed the parent_age attribute into intervals by dividing the values to be fall on one of two possible interval labels with equal width which is(40,50],(50,60] by discretization the values well be simpler to classify or perform other methods that can help us later in our model.

and to better utilize and interpret the grades attributes for each student, we have converted the numeric grades into letter grades (A+, A, B+, B, C+, C, D+, D, F). This transformation was undertaken to focus on the general letter grade representation rather than the precise numerical values.

Encoding

Dataset$parent_was_in_college[Dataset$parent_was_in_college=="TRUE"]<-1
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="True"]<-1
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="FALSE"]<-0
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="False"]<-0

Dataset$will_go_to_college[Dataset$will_go_to_college=="TRUE"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="True"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="FALSE"]<-1
Dataset$will_go_to_college[Dataset$will_go_to_college=="False"]<-1

Dataset$gender[Dataset$gender=="Female"]<-1
Dataset$gender[Dataset$gender=="Male"]<-0

Dataset$school_accreditation[Dataset$school_accreditation=="A"]<-1
Dataset$school_accreditation[Dataset$school_accreditation=="B"]<-0

Dataset$interest[Dataset$interest=="Very Interested"]<-4
Dataset$interest[Dataset$interest=="Interested"]<-3
Dataset$interest[Dataset$interest=="Less Interested"]<-2
Dataset$interest[Dataset$interest=="Not Interested"]<-1
Dataset$interest[Dataset$interest=="Uncertain"]<-0


Dataset$type_school[Dataset$type_school=="Academic"]<-1 
Dataset$type_school[Dataset$type_school=="Vocational"]<-0

Dataset$residence[Dataset$residence=="Urban"]<-1
Dataset$residence[Dataset$residence=="Rural"]<-0
print(Dataset)

Since encoding is an important step in data preprocessing that enables the use of categorical data in various data analysis and machine learning tasks, we encoded attributes like the ‘parent was in college’ attribute from (True, False) to (1, 0), and ‘will go to college’ from (True, False) to (0, 1). This encoding is carried out as we aim to predict the influencing factors. Additionally, we encoded the ‘gender’ attribute from (Female, Male) to (1, 0), ‘school accreditation’ from (A, B) to (1, 0), ‘type_school’ from (Academic, Vocational) to (1, 0), ‘residence’ from (Urban, Rural) to (1, 0), and ‘interest’ from (Very interested ,Interested , Less Interested , Not Interested ,Uncertain ) to (4,3,2, 1, 0) respectively. Encoding serves to simplify the data, reduce complexity, and enhance its suitability for modeling purposes.

Correlation analysis Chi square test for nominal attribute:


#1
C=chisq.test(Dataset$type_school , Dataset$will_go_to_college)
print(C)

    Pearson's Chi-squared test with Yates' continuity correction

data:  Dataset$type_school and Dataset$will_go_to_college
X-squared = 1.0751, df = 1, p-value = 0.2998
#2

C=chisq.test(Dataset$school_accreditation , Dataset$will_go_to_college)
print(C)

    Pearson's Chi-squared test with Yates' continuity correction

data:  Dataset$school_accreditation and Dataset$will_go_to_college
X-squared = 0.78513, df = 1, p-value = 0.3756
#3
C=chisq.test(Dataset$gender , Dataset$will_go_to_college)
print(C)

    Pearson's Chi-squared test with Yates' continuity correction

data:  Dataset$gender and Dataset$will_go_to_college
X-squared = 1.0249, df = 1, p-value = 0.3114
#4
C=chisq.test(Dataset$interest , Dataset$will_go_to_college)
print(C)

    Pearson's Chi-squared test

data:  Dataset$interest and Dataset$will_go_to_college
X-squared = 73.337, df = 4, p-value = 4.477e-15
#5
C=chisq.test(Dataset$residence , Dataset$will_go_to_college)
print(C)

    Pearson's Chi-squared test with Yates' continuity correction

data:  Dataset$residence and Dataset$will_go_to_college
X-squared = 0.016098, df = 1, p-value = 0.899
#6
C=chisq.test(Dataset$average_grades , Dataset$will_go_to_college)
print(C)

    Pearson's Chi-squared test

data:  Dataset$average_grades and Dataset$will_go_to_college
X-squared = 261.89, df = 4, p-value < 2.2e-16
#7
C=chisq.test(Dataset$parent_was_in_college , Dataset$will_go_to_college)
print(C)

    Pearson's Chi-squared test with Yates' continuity correction

data:  Dataset$parent_was_in_college and Dataset$will_go_to_college
X-squared = 2.1194, df = 1, p-value = 0.1454

All the attributes have X-square greater than the p-value which indicate a some association with the class label; therefore we reject the null hypothesis

we noticed for ‘interest’ and ‘average grade’ the analysis shows that X-square is much larger than p-value indicate the significant association of the two attributes with the decision of the student to go to the collage or not

Correlation coefficient analysis for numeric attribute:


biserial.cor(Dataset$parent_salary,Dataset$will_go_to_college, c("all.obs", "complete.obs"), level = 1)
[1] 0.4756928
biserial.cor(Dataset$house_area,Dataset$will_go_to_college, c("all.obs", "complete.obs"), level = 1)
[1] 0.4672669
biserial.cor(Dataset$parent_age,Dataset$will_go_to_college,c("all.obs", "complete.obs"), level = 1)
[1] 0.04287336
 
 

the analysis shows moderate correlation coefficient for parent salary and house area with the class label which indicate that they are relevant factors meaning that the higher the parent salary and the larger house area the higher probability for a student to enroll in a collage

where is the on other hand, the correlation coefficient for the parent age is very small which indicate that the parent age has little impact to the probability for student to enroll in a collage

Feature selection:

ultimately based on the analysis of the correlation that we conducted on the relationship of the dataset attributes with the class label, and the understanding of the data and the context of each attribute and potential relevance to the class label we decided to not delete any of the attribute

Classification:

The attribute selection measure is a heuristic technique employed to select the most optimal criteria for dividing a collection of data tuples into partitions that are as pure as possible. Several attribute selection measures are commonly utilized, such as Information Gain (ID3), Gain Ratio (C4.5), and Gini Index (CART). we will constraints for Each of these measures different partitions by dividing them in ratios of 50%-50%, 70%-30%, and 80%-20%.

factor the data



data<-Preprocessed_dataset
data$will_go_to_college <- as.factor(data$will_go_to_college)
data$residence <- as.factor(data$residence)
data$gender <- as.factor(data$gender)
data$parent_was_in_college <- as.factor(data$parent_was_in_college)
data$interest <- as.factor(data$interest)
data$type_school <- as.factor(data$type_school)
data$school_accreditation <- as.factor(data$school_accreditation)
data$average_grades <- as.factor(data$average_grades)

balanced or imbalanced


library(tidyverse) 
library(caret)
data$will_go_to_college<- as.numeric(data$will_go_to_college)
hist(data$will_go_to_college,col="coral")

prop.table(table(data$will_go_to_college))

  1   2 
0.5 0.5 

we want to confirm that the distribution between the two label data is not too much different. Because imbalanced datasets can lead to imbalanced accuracy.

Fortunately ,our data is balanced

70% training, 30% testing
set.seed(123)
ind=sample(2, nrow(data), replace=TRUE, prob=c(0.70 , 0.30))
train_data=data[ind==1,]
test_data=data[ind==2,]
dim(train_data)
[1] 705  11
dim(test_data)
[1] 295  11

We partition the data the data into (70% training, 30% testing). This result in 705 object in the training set and 295 object in the testing set.

information gain


library(party)
myFormula<- will_go_to_college~ + type_school+school_accreditation+gender+interest+residence+parent_age+parent_salary+house_area+average_grades+parent_was_in_college

dataset_ctree<-ctree(myFormula, data=train_data)
table(predict(dataset_ctree), train_data$will_go_to_college)
   
      0   1
  0 309  48
  1  37 311
plot(dataset_ctree,type="simple")

testPred <- predict(dataset_ctree, newdata = test_data)
result<-table(testPred, test_data$will_go_to_college )
library(caret)
co_result <- confusionMatrix(result,positive="1")
print(co_result)
Confusion Matrix and Statistics

        
testPred   0   1
       0 136  34
       1  18 107
                                          
               Accuracy : 0.8237          
                 95% CI : (0.7754, 0.8655)
    No Information Rate : 0.522           
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.6451          
                                          
 Mcnemar's Test P-Value : 0.03751         
                                          
            Sensitivity : 0.7589          
            Specificity : 0.8831          
         Pos Pred Value : 0.8560          
         Neg Pred Value : 0.8000          
             Prevalence : 0.4780          
         Detection Rate : 0.3627          
   Detection Prevalence : 0.4237          
      Balanced Accuracy : 0.8210          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 82.37%
Error rate 17.63%
Sensitivity 75.89%
Specificity 88.31%
Precision 85.60%

In this decision tree model, the average grade with the highest information gain is selected as the root attribute. This means that the average grade is considered the most informative feature for classifying the data.

To enhance the model’s predictive capability, additional splitting criteria are incorporated based on their information gain values. These criteria include type_school, interest, residence, parent_salary, and house_area. By considering these attributes, the model aims to capture more relevant information from the data and improve its classification accuracy.

The model then utilizes the house area and average grades as splitting criteria to separate the data into different branches of the tree in multiples levels

However, it’s worth noting that the decision tree in this case is considered to have a low level. This implies that the tree is relatively shallow and may not be able to capture intricate patterns or relationships present in the data. For example gender, parent_was_in_college, parent age, and school accreditation are not included in the tree due to limited availability of the data or their lower information gain values.

Gain ratio

library(RWeka)
C45Fit <- J48( will_go_to_college~.,data=train_data)
table(train_data$will_go_to_college, predict(C45Fit))
   
      0   1
  0 322  24
  1  27 332
plot(C45Fit,type = "simple")

testPred <- predict(C45Fit, newdata = test_data)
results <- confusionMatrix(testPred, test_data$will_go_to_college, positive = "1")
print(results)
Confusion Matrix and Statistics

          Reference
Prediction   0   1
         0 128  32
         1  26 109
                                          
               Accuracy : 0.8034          
                 95% CI : (0.7534, 0.8472)
    No Information Rate : 0.522           
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.6053          
                                          
 Mcnemar's Test P-Value : 0.5115          
                                          
            Sensitivity : 0.7730          
            Specificity : 0.8312          
         Pos Pred Value : 0.8074          
         Neg Pred Value : 0.8000          
             Prevalence : 0.4780          
         Detection Rate : 0.3695          
   Detection Prevalence : 0.4576          
      Balanced Accuracy : 0.8021          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 80.34%
Error rate 19.66%
Sensitivity 77.30%
Specificity 83.12%
Precision 80.74%

In contrast to the previous decision tree, the current tree is more complex and includes all the attributes except for gender. This means that the decision tree takes into account a wider range of factors to make its classifications. The attribute with the highest Gain ratio, which measures the effectiveness of a split, is chosen as the root attribute. In this case, Parent salary is determined to have the highest Gain ratio and is selected as the root attribute.

The model then repeatedly uses average grades as the splitting criterion at different levels of the tree. This indicates that the average grades are considered crucial for classifying the data and further subdividing the branches.

Gini index

library(rpart)
library(rpart.plot)
cart_fit=rpart(will_go_to_college~., data=train_data, method="class",cp=0.008)


rpart.plot(cart_fit)


testPred<- predict(cart_fit,newdata=test_data,type="class")
results<- confusionMatrix(testPred,test_data$will_go_to_college,positive="1")
print(results)
Confusion Matrix and Statistics

          Reference
Prediction   0   1
         0 125  31
         1  29 110
                                          
               Accuracy : 0.7966          
                 95% CI : (0.7461, 0.8411)
    No Information Rate : 0.522           
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.5922          
                                          
 Mcnemar's Test P-Value : 0.8973          
                                          
            Sensitivity : 0.7801          
            Specificity : 0.8117          
         Pos Pred Value : 0.7914          
         Neg Pred Value : 0.8013          
             Prevalence : 0.4780          
         Detection Rate : 0.3729          
   Detection Prevalence : 0.4712          
      Balanced Accuracy : 0.7959          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 79.66%
Error rate 20.34%
Sensitivity 78.01%
Specificity 81.17%
Precision 79.14%

In this particular partition of the decision tree, Parent salary was once again chosen as the root attribute due to its high Gain index.

Gender and residence, on the other hand, were not included in this partition of the tree. Without sufficient data or a significant information gain associated with these attributes, the model may not have enough evidence to utilize them for accurate classification.

It’s worth noting that gender was not included in this partition, and it is important to highlight that the accuracy of this specific model was relatively lower, at 79.66%.

80% training, 20% testing
set.seed(123)
ind=sample(2, nrow(data), replace=TRUE, prob=c(0.80 , 0.20))
train_data=data[ind==1,]
test_data=data[ind==2,]
dim(train_data)
[1] 802  11
dim(test_data)
[1] 198  11

We partition the data the data into (80% training, 20% testing). This result in 802 object in the training set and 198 object in the testing set.

information gain

library(party)
myFormula<- will_go_to_college~ + type_school+school_accreditation+gender+interest+residence+parent_age+parent_salary+house_area+average_grades+parent_was_in_college

dataset_ctree<-ctree(myFormula, data=train_data)
table(predict(dataset_ctree), train_data$will_go_to_college)
   
      0   1
  0 344  41
  1  56 361
plot(dataset_ctree,type="simple")

testPred <- predict(dataset_ctree, newdata = test_data)
result<-table(testPred, test_data$will_go_to_college )
library(caret)
co_result <- confusionMatrix(result,positive="1")
print(co_result)
Confusion Matrix and Statistics

        
testPred  0  1
       0 81 13
       1 19 85
                                          
               Accuracy : 0.8384          
                 95% CI : (0.7796, 0.8868)
    No Information Rate : 0.5051          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.6769          
                                          
 Mcnemar's Test P-Value : 0.3768          
                                          
            Sensitivity : 0.8673          
            Specificity : 0.8100          
         Pos Pred Value : 0.8173          
         Neg Pred Value : 0.8617          
             Prevalence : 0.4949          
         Detection Rate : 0.4293          
   Detection Prevalence : 0.5253          
      Balanced Accuracy : 0.8387          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 83.84 %
Error rate 16.16%
Sensitivity 86.73%
Specificity 81.00%
Precision 81.73%

In this partition of the decision tree, the average grade with the highest information gain is selected as the root attribute. The selection is based on the fact that the average grade attribute has the lowest conditional entropy, the model aims to create a more effective split and improve the classification accuracy. also to mention that attribute was not included in the tree gender,parent was in collage, parent age

On a positive note, the accuracy in this partition has increased by 1.47% compared to the previous partition. This improvement in accuracy can be attributed to the fact that the training data has been increased by 10%. By having more data for training, the model has a larger and more diverse set of examples to learn from, allowing it to make more accurate predictions.

Gain ratio

library(RWeka)


C45Fit <- J48( will_go_to_college~.,data=train_data)
table(train_data$will_go_to_college, predict(C45Fit))
   
      0   1
  0 378  22
  1  16 386
plot(C45Fit,type = "simple")

testPred <- predict(C45Fit, newdata = test_data)
results <- confusionMatrix(testPred, test_data$will_go_to_college, positive = "1")
print(results)
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 76 15
         1 24 83
                                         
               Accuracy : 0.803          
                 95% CI : (0.7407, 0.856)
    No Information Rate : 0.5051         
    P-Value [Acc > NIR] : <2e-16         
                                         
                  Kappa : 0.6064         
                                         
 Mcnemar's Test P-Value : 0.2002         
                                         
            Sensitivity : 0.8469         
            Specificity : 0.7600         
         Pos Pred Value : 0.7757         
         Neg Pred Value : 0.8352         
             Prevalence : 0.4949         
         Detection Rate : 0.4192         
   Detection Prevalence : 0.5404         
      Balanced Accuracy : 0.8035         
                                         
       'Positive' Class : 1              
                                         

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 80.03%
Error rate 19.97%
Sensitivity 84.69%
Specificity 76.00%
Precision 77.57%

In this particular model that utilizes the gain ratio, it is observed that the decision tree is once again more complex compared to the other models. The attribute chosen as the root attribute is parent salary, based on its high purity and resulting high gain ratio. However, it is important to note that the gender attribute was not included in the decision tree. Ultimately, the gain ratio model’s construction involved careful considerations and prioritized the parent’s salary as root

Gini index

library(rpart)
library(rpart.plot)
cart_fit=rpart(will_go_to_college~., data=train_data, method="class",cp=0.008)


rpart.plot(cart_fit)


testPred<- predict(cart_fit,newdata=test_data,type="class")
results<- confusionMatrix(testPred,test_data$will_go_to_college,positive="1")
print(results)
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 85 23
         1 15 75
                                          
               Accuracy : 0.8081          
                 95% CI : (0.7462, 0.8605)
    No Information Rate : 0.5051          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.6158          
                                          
 Mcnemar's Test P-Value : 0.2561          
                                          
            Sensitivity : 0.7653          
            Specificity : 0.8500          
         Pos Pred Value : 0.8333          
         Neg Pred Value : 0.7870          
             Prevalence : 0.4949          
         Detection Rate : 0.3788          
   Detection Prevalence : 0.4545          
      Balanced Accuracy : 0.8077          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 80.81%
Error rate 19.19%
Sensitivity 76.53%
Specificity 85.00%
Precision 83.33%

for the first time house area was chosen as the root due to its high purity, as indicated by the high Gini index.

The model relies on the parent_salary attribute to split the tree multiple times, indicating its importance in determining the classification. This suggests that the parent’s salary has a significant impact on whether a student will go to college or not, and it is repeatedly used as a criterion for further branching in the tree.

It’s worth noting that 20% of the dataset follows the rule:

IF house_area < 0.54 and parent_salary < 0.42, THEN will go to college = 1.

This rule implies that when the house area is below a certain threshold and the parent’s salary is below another threshold, there is a higher likelihood of the student going to college.

Moreover, 28% of the dataset follows the rule:

IF house_area >= 0.54 and parent_salary >= 0.37 and type_school = 1, THEN will go to college = 1. This rule suggests that when the house area is above a certain threshold, the parent’s salary is above another threshold, and the type of school is 1, there is a higher probability of the student going to college.

Overall, these rules cover a significant portion of the dataset, 48% of it. This indicates that a large amount of data can be classified based on these specific rules, providing valuable insights into the factors that influence a student’s likelihood of attending college. we only mentioned those two rules duo to their high percentage of data

50% training, 50% testing
set.seed(123)
ind=sample(2, nrow(data), replace=TRUE, prob=c(0.50 , 0.50))
train_data=data[ind==1,]
test_data=data[ind==2,]
dim(train_data)
[1] 493  11
dim(test_data)
[1] 507  11

We partition the data the data into (50% training, 50% testing). This result in 493 object in the training set and 507 object in the testing set.

information gain

library(party)
myFormula<- will_go_to_college~ + type_school+school_accreditation+gender+interest+residence+parent_age+parent_salary+house_area+average_grades+parent_was_in_college

dataset_ctree<-ctree(myFormula, data=train_data)
table(predict(dataset_ctree), train_data$will_go_to_college) 
   
      0   1
  0 211  23
  1  40 219
plot(dataset_ctree,type="simple")

testPred <- predict(dataset_ctree, newdata = test_data)
result<-table(testPred, test_data$will_go_to_college )

library(caret)
co_result <- confusionMatrix(result, positive = "1")
print(co_result)
Confusion Matrix and Statistics

        
testPred   0   1
       0 196  27
       1  53 231
                                          
               Accuracy : 0.8422          
                 95% CI : (0.8075, 0.8729)
    No Information Rate : 0.5089          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.6837          
                                          
 Mcnemar's Test P-Value : 0.005189        
                                          
            Sensitivity : 0.8953          
            Specificity : 0.7871          
         Pos Pred Value : 0.8134          
         Neg Pred Value : 0.8789          
             Prevalence : 0.5089          
         Detection Rate : 0.4556          
   Detection Prevalence : 0.5602          
      Balanced Accuracy : 0.8412          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 84.22%
Error rate 15.78%
Sensitivity 89.53%
Specificity 78.71%
Precision 81.34%

In this decision tree model, the root attribute is determined by selecting the average grade with the highest information gain. This indicates that the average grade is considered the most informative feature for classifying the data, as it provides significant insights into the classification process.

To improve the model’s predictive capability, additional splitting criteria are incorporated based on their information gain values. These criteria include type_school, interest, residence, parent_salary, house_area, and whether the parent was in college. By considering these attributes, the model aims to capture more relevant information and enhance its ability to classify the data accurately.

In the tree structure, the model utilizes both the house area and average grades as splitting criteria in multiple levels. This means that the data is divided into different branches of the tree based on these attributes, allowing for more refined classification.

gender, parent age, and school accreditation are not included in the tree due to either limited availability of the data or their lower information gain values, indicating that they may have less impact on the classification process according to the model’s evaluation.

Gain ratio

library(RWeka)
C45Fit <- J48( will_go_to_college~.,data=train_data)
table(train_data$will_go_to_college, predict(C45Fit))
   
      0   1
  0 234  17
  1  13 229
plot(C45Fit,type = "simple")

testPred <- predict(C45Fit, newdata = test_data)
results <- confusionMatrix(testPred, test_data$will_go_to_college, positive = "1")
print(results)
Confusion Matrix and Statistics

          Reference
Prediction   0   1
         0 206  32
         1  43 226
                                          
               Accuracy : 0.8521          
                 95% CI : (0.8181, 0.8818)
    No Information Rate : 0.5089          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.7038          
                                          
 Mcnemar's Test P-Value : 0.2482          
                                          
            Sensitivity : 0.8760          
            Specificity : 0.8273          
         Pos Pred Value : 0.8401          
         Neg Pred Value : 0.8655          
             Prevalence : 0.5089          
         Detection Rate : 0.4458          
   Detection Prevalence : 0.5306          
      Balanced Accuracy : 0.8516          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

Evaluation method Method
Accuracy 85.21%
Error rate 14.79%
Sensitivity 87.60%
Specificity 82.73%
Precision 84.01%

In contrast to the previous decision tree, the current tree is more complex and includes all the attributes except for parent age. Notably, for the first time, gender is included as an attribute in the tree. This indicates that the decision tree now takes into account a wider range of factors, including gender, to make its classifications. By incorporating these additional attributes, the model aims to capture more nuanced patterns and relationships in the data.

The attribute with the highest Gain ratio is chosen as the root attribute. In this particular case, Parent salary is determined to have the highest Gain ratio and is selected as the root attribute. This suggests that Parent salary provides the most significant reduction in uncertainty and is considered crucial for classifying the data accurately.

Gini index

library(rpart)
library(rpart.plot)
cart_fit=rpart(will_go_to_college~., data=train_data, method="class",cp=0.008)

rpart.plot(cart_fit)


testPred<- predict(cart_fit,newdata=test_data,type="class")
results<- confusionMatrix(testPred,test_data$will_go_to_college,positive="1")
print(results)
Confusion Matrix and Statistics

          Reference
Prediction   0   1
         0 204  30
         1  45 228
                                          
               Accuracy : 0.8521          
                 95% CI : (0.8181, 0.8818)
    No Information Rate : 0.5089          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.7037          
                                          
 Mcnemar's Test P-Value : 0.106           
                                          
            Sensitivity : 0.8837          
            Specificity : 0.8193          
         Pos Pred Value : 0.8352          
         Neg Pred Value : 0.8718          
             Prevalence : 0.5089          
         Detection Rate : 0.4497          
   Detection Prevalence : 0.5385          
      Balanced Accuracy : 0.8515          
                                          
       'Positive' Class : 1               
                                          

this matrix below show the evaluation model on testing data

The attribute chosen as the root attribute in this decision tree is average grades, primarily due to its high purity, indicating a high Gini index. The Gini index measures the impurity of a node in a decision tree, and a high purity value suggests that the attribute is effective for classification.
Evaluation method Method
Accuracy 85.21%
Error rate 24.79%
Sensitivity 88.37%
Specificity 81.93%
Precision 83.52%

27% of the dataset follows the rule:

IF average_grades=+A,+B,A and parent_salary>=0.37 and house_area>=0.41 and type_school=1, THEN will_go_to_collage=0.

Furthermore, 17% of the dataset follows the rule:

IF average_grades=B,+C and house_area<=0.41, THEN will_go_to_collage=1.

These rules highlight the complexity and variability in determining whether a student will go to college. Even if a student has stable financial circumstances and good grades, it does not guarantee their enrollment in college. in contract to the second which mean Other factors could play a role in the decision-making process.

we only mentioned those two rules duo to their high percentage of data

final analysis

information gain

Information Gain is a measure used in the ID3 algorithm for attribute selection in decision tree learning. It quantifies the amount of information obtained about the class variable when a given attribute is used to split the data. The Information Gain is calculated by substract the entropy Expected information needed to classify a tuple in D a with Conditional Entropy Information needed after using A to split D into v partitions, to classify D . A higher Information Gain indicates that the attribute is more informative and should be chosen as the splitting criterion.

for our trees,

average grade was chosen as the root attribute for all 3 trees with the highest information gain This attribute Reflects the least randomness or “impurity” in these partitions and guarantees that a simple tree is found.

50%-50% 70%-30% 80%-20%
Accuracy 84.22% 82.37 % 83.84 %
Error rate 15.78% 17.63% 16.16%
Sensitivity 89.53% 75.89% 86.73%
Specificity 78.71% 88.31% 81.00%
Precision 81.34% 85.60% 81.73%

the evaluation method table shows that the best partiton for information gain shows that 50%-50% is the best model based on accuracy(tuples that are correctly classified) with precntages 84.22% and Sensitivity (True positive recognition rate) with 89.53% but the lowest Specificity(True negative recognition rate) with 78.71%. overall it’s the best model for information gain has the lowest error rate 15.78%

gain ratio

Gain Ratio is an normaliztion for Information Gain and is used in the C4.5 algorithm, which is an extension of ID3. While Information Gain tends to favor attributes with many multivariate It is calculated by dividing the Information Gain by SplitInfo a(D).

for our trees,

Parent salary was chosen as the root attribute for all 3 trees due to its high Gain ratio, the trees split are unbalanced since gain ratio Tends to prefer unbalanced splits in which one partition is much smaller than the others

50%-50% 70%-30% 80%-20%
Accuracy 85.21% 80.34% 80.03%
Error rate 14.79% 19.66% 19.97%
Sensitivity 87.60% 77.30% 84.69%
Specificity 82.73% 83.12% 76.00%
Precision 84.01% 80.74% 77.57%

the evaluation method table shows that the best partition for gain ratio shows that 50%-50% is the best model based on accuracy(tuples that are correctly classified) with percntages 85.21% and Sensitivity (True positive recognition rate) with 87.60% and Precision(tuples labeled as positive are actually positive) with percentages 84.01% but the lowest Specificity(True negative recognition rate) with 82.73%. overall it’s the best model dor gain ratio with the lowest error rate 14.79%

gini index

Gini Index is a criterion used in the CART (Classification and Regression Trees) algorithm for attribute selection. It measures the impurity of a set of tuples, with lower values Gini a(D) indicating higher purity. The Gini Index is calculated by summing the squared probabilities of each class label in the set

for our trees,

gini index had different root (parent salary/house area/averages grades) for each tree

50%-50% 70%-30% 80%-20%
Accuracy 85.21% 79.66% 80.81%
Error rate 14.79% 20.34% 19.19%
Sensitivity 88.37% 78.01% 76.53%
Specificity 81.93% 81.17% 85.00%
Precision 83.52% 79.14% 83.33%

the evaluation method table shows that the best partition for gain ratio shows that 50%-50% is the best model based on accuracy(tuples that are correctly classified) with percentages 85.21% and Sensitivity (True positive recognition rate) with 88.37% and Precision(tuples labeled as positive are actually positive) with percentages 83.52% overall it’s the best model for gini index with the lowest error rate 14.79%

noticeable notes

important to note that gender was not included in 7 of 9 trees which

parent’s salary was the root of 4 of 9 trees

average grades was the root of 4 of 9 trees

house area was the root only once 1 of 9 tree

In summary, the analysis of the decision trees reveals that financial circumstances, as indicated by parent’s salary, and academic performance, as indicated by average grades, are crucial factors for contemporary universities when predicting college enrollment. In this particular dataset, gender is not considered highly important in the decision-making process.

best model

the 50% training, 50% testing was the best partition for all attribute selection measures with accuracies 85.21%. for both gini index and gain ratio

Both attribute selection measures yield the same accuracy and error rate, so we need to check the remaining evaluation metrics that is provided:

gain ratio gini index
Accuracy 85.21% 85.21%
Error rate 14.79% 14.79%
Sensitivity 87.60% 88.37%
Specificity 82.73% 81.93%
Precision 84.01% 83.52%

Comparing the sensitivity, specificity, and precision:

- Sensitivity: Gini Index has a higher sensitivity value (88.37%) compared to Gain Ratio (87.60%). Higher sensitivity indicates a better ability to correctly identify positive instances so gini index is better regarding Sensitivity.

- Specificity: Gini Index has a lower specificity value (81.93%) compared to Gain Ratio (82.73%). Higher specificity indicates a better ability to correctly identify negative instances.

- Precision: Gain Ratio has a higher precision value (84.01%) compared to Gini Index (83.52%). Higher precision indicates a better ability to correctly classify positive instances.

so gain ratio is better regarding Specificity and Precision .

Based on these comparisons, it is difficult to determine definitively which attribute selection measure is the best. The choice between Gini Index and Gain Ratio may depend on the specific requirements and priorities of the problem but since we focus on the true negative more than true positive because our goal of classification to determine who will not go to collage, so we will choose the model based on the Specificity and Precision (Gain ratio)

our final tree is:

Clustering Analysis:

In this analysis, we apply K-means clustering to the dataset using different values of K. K-means clustering is an unsupervised learning algorithm that partitions the data into K clusters based on similarity. We will explore three different values of K and evaluate the clustering results using various metrics.

Removing the class label and preparing the dataset for Clustering

 
original_data <- Preprocessed_dataset

# Remove any non-numeric attributes
numeric_data <- original_data[, sapply(original_data, is.numeric)]

# Remove the class label 'will_go_to_college'
numeric_data <- numeric_data[, !(names(numeric_data) == 'will_go_to_college')]

Now, the ‘numeric_dataset’ dataset contains only numeric attributes without the class label, which makes it ready for the clustering process.

___________________________________________________________________________________

K=2

In our exploration of k-means clustering, we will start by examining the dataset with K=2, representing an initial attempt to partition the data into two distinct clusters. This initial analysis will serve as a foundational step in understanding the inherent structure and patterns

# k-means clustering set a seed for random number generation to make the results reproducible 
set.seed(8953)

# run kmeans clustering to find 2 clusters
kmeans.result <- kmeans(numeric_data, 2)

# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

NA
NA
NA

The Silhouette coefficient

#average for each cluster 
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data)) 

#k-means clustering with estimating k and initializations 
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares

# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 8669.059 
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4)  

cluster_assignments <- kmeans.result$cluster
 

BCubed recall and precision

# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
  cluster_indices <- which(true_labels == i)
  precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))

# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
  cluster_indices <- which(cluster_assignments == j)
  recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))

cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.002333333 
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.1166667 

Cluster Analysis for K=2:

  • Silhouette Width: 0.36

    • Indicates relatively well-separated and distinct clusters.
  • Within-Cluster Sum of Squares: 8669.059

    • Moderate compactness; clusters are not too tight.
  • BCubed Precision: 0.0023

    • Low precision suggests potential misassignments.
  • BCubed Recall: 0.1167

    • Low recall indicates incomplete representation of true clusters.

So, the clusters are moderately compact with a reasonable silhouette width, but low precision and recall suggest potential misclassification issues.

___________________________________________________________________________________

K=5

Moving to K=5, our exploration in k-means clustering advances to a configuration with five clusters. This adjustment allows for a more detailed partitioning of the dataset, potentially revealing finer grained patterns and distinctions among the data points. The analysis at K=5 will offer insights into the nature of these clusters, assessing their characteristics, compactness, and separation, as we strive to optimize the clustering configuration for the specific nuances of our dataset.

# k-means clustering set a seed for random number generation to make the results reproducible 
set.seed(8953)

# run kmeans clustering to find 4 clusters
kmeans.result <- kmeans(numeric_data, 5)

# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

NA
NA
NA

The Silhouette coefficient

#average for each cluster 
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data)) 

#k-means clustering with estimating k and initializations 
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares

# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 4303.841 
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4)  

cluster_assignments <- kmeans.result$cluster
 

BCubed recall and precision

# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
  cluster_indices <- which(true_labels == i)
  precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))

# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
  cluster_indices <- which(cluster_assignments == j)
  recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))

cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.001666667 
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.1333333 

Cluster Analysis for K=5:

  • Silhouette Width: 0.29

    • Indicates less well-separated clusters compared to k=2
  • Within-Cluster Sum of Squares: 4303.841

    • Lower within-cluster sum suggests more compact clusters than k=2
  • BCubed Precision: 0.00166

    • Low precision implies potential misclassifications.
  • BCubed Recall: 0.1333

    • Slightly higher recall compared to k=2, but still low.

Final Analysis: Clusters are more compact than k=2 but less well-separated. Precision and recall remain low, indicating potential misclassifications.

___________________________________________________________________________________

K=7

Proceeding to k=7, our investigation in k-means clustering expands further as we explore a configuration with seven clusters. This adjustment aims to capture even more nuanced patterns and variations within the dataset. Analyzing the characteristics of the seven clusters will provide a more granular understanding of the underlying structure, potentially revealing subtleties that might not be as evident with fewer clusters. As we delve into k=7, our objective is to refine our clustering configuration to align more closely with the intricate details present in the dataset.

# k-means clustering set a seed for random number generation to make the results reproducible 
set.seed(8953)

# run kmeans clustering to find 2 clusters
kmeans.result <- kmeans(numeric_data, 7)

# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

NA
NA
NA
NA

The Silhouette coefficient

#average for each cluster 
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data)) 

#k-means clustering with estimating k and initializations 
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares

# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 3389.656 
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4)  

cluster_assignments <- kmeans.result$cluster
 

BCubed recall and precision

# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
  cluster_indices <- which(true_labels == i)
  precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))

# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
  cluster_indices <- which(cluster_assignments == j)
  recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))

cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.0006666667 
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.06666667 

Cluster Analysis for k=7:

  • Silhouette Width: 0.27

    • Indicates clusters are less well-separated compared to k=2 and k=5
  • Within-Cluster Sum of Squares: 3389.656

    • Lower within-cluster sum suggests more compact clusters than k=5
  • BCubed Precision: 0.00067

    • Very low precision indicating significant misclassifications.
  • BCubed Recall: 0.067

    • Low recall suggesting incomplete representation of true clusters.

Final Analysis: Clusters are less well-separated than k=2 and k=5 and while more compact than k=5, precision and recall remain low, indicating potential misclassifications.

___________________________________________________________________________________

Elbow Method Insight:

We implemented the Elbow Method for determining the optimal number of clusters (k) in a k-means clustering algorithm.

# Function to calculate total within-cluster sum of squares (wss)
wss <- function(k) {
  kmeans_result <- kmeans(numeric_data, centers = k, nstart = 10)  # You can adjust nstart based on your preference
  return(sum(kmeans_result$tot.withinss))
}

# Calculate the total within-cluster sum of squares for different values of k
k_values <- 1:10  # You can adjust the range of k values
wss_values <- sapply(k_values, wss)

# Plot the elbow curve
plot(k_values, wss_values, type = "b", pch = 19, frame = FALSE, 
     xlab = "Number of Clusters (k)", ylab = "Total Within-Cluster Sum of Squares (WSS)",
     main = "Elbow Method")

NA
NA

The elbow method has indicated that K=5 is a reasonable choice in terms of balancing the trade-off between capturing variance and not overly complicating the model with too many clusters.

Findings

Classification:

50%-50% 70%-30% 80%-20%
ig igr gini ig igr gini ig igr gini
Accuracy 84.22% 85.21% 85.21% 82.37 % 80.34% 79.66% 83.84 % 80.03% 80.81%
Error rate 15.78% 14.79% 14.79% 17.63% 19.66% 20.34% 16.16% 19.97% 19.19%
Sensitivity 89.53% 87.60% 88.37% 75.89% 77.30% 78.01% 86.73% 84.69% 76.53%
Specificity 78.71% 82.73% 81.93% 88.31% 83.12% 81.17% 81.00% 76.00% 85.00%
Precision 81.34% 84.01% 83.52% 85.60% 80.74% 79.14% 81.73% 77.57% 83.33%

Accuracy (recognition rate): Percentage of test set tuples that are correctly classified

Error rate (misclassification rate): 1 – accuracy

Sensitivity (recall): True positive recognition rate

Specificity: True negative recognition rate

Precision (exactness): What % of tuples labeled as positive are actually positive.

For partition 50%-50% : gain ratio and gini index equals in accuracy 85.21% but as we mention before in best model we choose based on Precision 84.01% and Specificity 82.73% , Gain rato is the best model.

For partition 70%-30%: information gain is the best model for this partition with accuracy 82.37 %

For partition 80%-20%: information gain is the best model for this partition with accuracy 83.84 %

overall 50%-50% wasa the best partition with algorithm gini index with accuracy 85.21%.

Clustering

Metric K=2 K=5 K=7
Average Silhouette Width 0.36 0.29 0.27
Total Within-Cluster Sum of Squares 8669.059 4303.841 3389.656
BCubed Precision 0.002333333 0.001666667 0.00067
BCubed Recall 0.1166667 0.1333333 0.067

In our overall analysis, we evaluated the performance of the k-means clustering algorithm for three different values of k (2, 5, and 7). The key metrics provide insights into the characteristics of the resulting clusters:

  1. Average Silhouette Width:

    • The average silhouette width measures the separation and cohesion of clusters. As k increases from 2 to 7, there is a gradual decline in silhouette width, indicating that clusters become less distinct. However, k=5 still maintains a reasonable silhouette width, suggesting a balance between separation and cohesion.
  2. Total Within-Cluster Sum of Squares:

    • The total within-cluster sum of squares reflects the compactness of clusters. Notably, k=7 exhibits the lowest sum of squares, implying more compact clusters compared to k=5 and k=2. This aligns with the expectation that a higher k tends to yield more compact clusters.
  3. BCubed Precision and Recall:

    • BCubed precision and recall metrics assess the accuracy and completeness of clustering. Precision and recall values are relatively low across all k values, indicating potential misclassifications. However, k=5 stands out with slightly higher precision and recall compared to k=2 and k=7, suggesting a better balance between accuracy and completeness.

Considering the results obtained from the elbow method, which identifies k=5 as the optimal number of clusters, our analysis aligns with this recommendation. The detailed examination of silhouette width, within-cluster sum of squares, and BCubed metrics reinforces the choice of k=5 as a suitable configuration, striking a balance between cluster distinctiveness and internal cohesion. This comprehensive evaluation supports the decision to proceed with k=5 for a more refined and effective clustering solution.

In the following figures, the visualisation of the clusters for each trial:

K=2:

k=5

K=7

Classification vs Clustering

classification offers a more interpretable and actionable solution compared to clustering, as it assigns clear labels to data points. In addition, the availability of class labels enhances the model’s ability to generalize and make informed predictions. While clustering struggle with overlapping clusters and lack of clear evaluation metrics, classification leverages the labeled data to optimize performance and provide meaningful insights. Therefore, for our dataset, the utilization of classification is not only justified by its high accuracy but also by its ability to address the specific characteristics and challenges presented in the data.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpJbXBvcnRhbnQgcGFja2FnZXM6DQoNCmBgYHtyfQ0Kc2V0d2QoIkRhdGFzZXQiKQ0KRGF0YXNldCA8LSByZWFkLmNzdigiZGF0YS5jc3YiKQ0KUHJlcHJvY2Vzc2VkX2RhdGFzZXQgPC0gIHJlYWQuY3N2KCJwcmVwcm9jZXNzZWRfZGF0YXNldC5jc3YiKSANCmlmKCFyZXF1aXJlKGdncGxvdDIpKXsNCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKX0NCmxpYnJhcnkoZ2dwbG90MikNCmlmKCFyZXF1aXJlKGRwbHlyKSl7DQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpfQ0KbGlicmFyeShkcGx5cikgDQoNCmlmKCFyZXF1aXJlKGx0bSkpew0KaW5zdGFsbC5wYWNrYWdlcygibHRtIil9DQpsaWJyYXJ5KGx0bSkNCmlmICghcmVxdWlyZShjbHVzdGVyLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiY2x1c3RlciIpDQp9DQppZiAoIXJlcXVpcmUoZmFjdG9leHRyYSwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiKQ0KfQ0KIA0KaWYgKCFyZXF1aXJlKGNhcmV0LCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KfQ0KaWYgKCFyZXF1aXJlKHBhcnR5LCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGFydHkiKQ0KfQ0KaWYgKCFyZXF1aXJlKHBhcnR5a2l0LCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGFydHlraXQiKQ0KfQ0KDQppZiAoIXJlcXVpcmUocnBhcnQsIHF1aWV0bHkgPSBUUlVFKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydCIpDQp9DQppZiAoIXJlcXVpcmUocnBhcnQucGxvdCwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKQ0KfQ0KaWYgKCFyZXF1aXJlKHRpZHl2ZXJzZSwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQp9DQoNCmlmICghcmVxdWlyZShSV2VrYSwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoIlJXZWthIikNCn0NCmBgYA0KDQojIElUMzI2IFByb2plY3Qge3N0eWxlPSJjb2xvcjogZ3JheSJ9DQoNClxfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cXw0KDQojIyBUaGUgR29hbCBhbmQgcHJvYmxlbQ0KDQpPdXIgcHJpbWFyeSBvYmplY3RpdmUgb2YgdGhpcyBhbmFseXNpcyBpcyB0byBjbGFzc2lmeSB3aGF0ZXZlciBhIHN0dWRlbnQgd2lsbCBnbyB0byBjb2xsZWdlIG9yIG5vdCB3ZSBhaW0gdG8gZGlzY292ZXIgdGhlIG1vc3QgaW5mbHVlbnRpYWwgdmFyaWFibGVzIGFuZCB0aGVpciByZWxhdGlvbnNoaXBzIHdpdGggdGhlIGRlY2lzaW9uIG5vdCB0byBhdHRlbmQgY29sbGVnZSB0byBwcm92aWRlIGNvdW5zZWxpbmcgc2Vzc2lvbiB0byB0aGUgc3R1ZGVudCB0byBoZWxwIHRoZW0NCg0KIyMgRGF0YSBtaW5pbmcgVGFzaw0KDQp1c2luZyB0aGUgY2xhc3NpZmljYXRpb24gbWV0aG9kcyBhbmQgdG8gaWRlbnRpZnkgdGhlIG1haW4gZmFjdG9ycyBhbmQgcmVhc29ucyB3aHkgc3R1ZGVudHMgYXJlIGxlc3MgbGlrZWx5IHRvIHB1cnN1ZSBoaWdoZXIgZWR1Y2F0aW9uIHVzaW5nIGNsYXNzIGxhYmVsIGluZGljYXRlZCAid2lsbF9nb190b19jb2xsZWdlIiBiZWluZyAnRmFsc2UnLiBCeSBsZXZlcmFnaW5nIHRoZSBwcm92aWRlZCBkYXRhc2V0IHdpdGggYXR0cmlidXRlcyBzdWNoIGFzIHNjaG9vbCB0eXBlLCBzY2hvb2wgYWNjcmVkaXRhdGlvbiwgZ2VuZGVyLCBpbnRlcmVzdCBpbiBjb2xsZWdlLCByZXNpZGVuY2UgYW5kIHVzZSBjbHVzdGVyaW5nIHRvIGdyb3VwIHBhdHRlcm4gb2YgdGhlIHN0dWRlbnQgd2hvIHdpbGwgZ28gdG8gY29sbGFnZSBvciBub3QNCg0KIyMgVGhlIFNvdXJjZQ0KDQpLYWdnbGUuY29tDQoNCiMjIFVSTCA6DQoNCjxodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL3NhZGRhbWF6eWF6eS9nby10by1jb2xsZWdlLWRhdGFzZXQ+DQoNCiMjIEdlbmVyYWwgaW5mb3JtYXRpb24NCg0KLSAgIE51bWJlciBvZiBhdHRyaWJ1dGVzIDogMTENCi0gICBOdW1iZXIgb2Ygcm93cyAob2JqZWN0cykgOiAxMDAwDQotICAgVGhlIGNsYXNzIGxhYmVsOiBUaGUgY2xhc3MgbGFiZWwgaW4gb3VyIHByb2plY3QgaXMgdGhlIGF0dHJpYnV0ZSAid2lsbF9nb190b19jb2xsZWdlIi4gVGhpcyBhdHRyaWJ1dGUgaXMgYmluYXJ5LCBtZWFuaW5nIHRoYXQgaXQgY2FuIHRha2Ugb24gdHdvIHZhbHVlczogVHJ1ZSBmb3IgeWVzIG9yIEZhbHNlIGZvciBuby4gVGhlIHZhbHVlIG9mIHRoaXMgYXR0cmlidXRlIHdpbGwgYmUgdGhlIHRhcmdldCB2YXJpYWJsZSB0aGF0IHdlIGFyZSB0cnlpbmcgdG8gcHJlZGljdCBkdXJpbmcgb3VyIHByb2plY3QuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgQXR0cmlidXRlICAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFR5cGUgICAgICB8IFBvc3NpYmxlIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHR5cGVfc2Nob29sICAgICAgICAgICB8IFRoZSB0eXBlIG9mIHNjaG9vbCB0aGUgc3R1ZGVudCBhdHRlbmRzICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgICAgfCBBY2FkZW1pYyAvIFZvY2F0aW9uYWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBzY2hvb2xfYWNjcmVkaXRhdGlvbiAgfCBUaGUgcXVhbGl0eSBpZiBzY2hvb2wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgQSAvIEIgKEEgaXMgYmV0dGVyIHRoYW4gQikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgZ2VuZGVyICAgICAgICAgICAgICAgIHwgVGhlIHN0dWRlbnQncyBnZW5kZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEJpbmFyeSAgICB8IE1hbGUgLyBGZW1hbGUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGludGVyZXN0ICAgICAgICAgICAgICB8IFRoZSBzdHVkZW50J3MgaW50ZXJlc3QgaW4gZ29pbmcgdG8gY29sbGVnZSAgICAgICAgICAgICAgICAgfCBOb21pbmFsICAgfCBWZXJ5IGludGVyZXN0ZWQgL0ludGVyZXN0ZWQgLyBMZXNzIEludGVyZXN0ZWQgLyBOb3QgSW50ZXJlc3RlZCAvVW5jZXJ0YWluIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCByZXNpZGVuY2UgICAgICAgICAgICAgfCBUaGUgc3R1ZGVudCdzIHJlc2lkZW5jZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgVXJiYW4gLyBSdXJhbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgcGFyZW50X2FnZSAgICAgICAgICAgIHwgVGhlIGFnZSBvZiB0aGUgc3R1ZGVudCdzIHBhcmVudHMgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE51bWVyaWMgICB8IDQwIC0gNjUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHBhcmVudF9zYWxhcnkgICAgICAgICB8IFRoZSBtb250aGx5IHNhbGFyeSBvZiB0aGUgc3R1ZGVudCdzIHBhcmVudHMgaW4gSURSL1J1cGlhaC4gfCBOdW1lcmljICAgfCAxMDAwSyAtIDEwTSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgfCBbMVJ1cGlhaCA9IDAuMDAwMjRTQVJdICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgaG91c2VfYXJlYSAgICAgICAgICAgIHwgVGhlIHNpemUgb2YgdGhlIHN0dWRlbnQncyBob3VzZSBpbiBtZXRlciBzcXVhcmUgICAgICAgICAgICB8IE51bWVyaWMgICB8IDIwIC0gMTIwICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGF2ZXJhZ2VfZ3JhZGVzICAgICAgICB8IFRoZSBzdHVkZW50J3MgYXZlcmFnZSBncmFkZXMgaW4gc2Nob29sLiAgICAgICAgICAgICAgICAgICAgfCBOdW1lcmljICAgfCA3NSAtIDk4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBwYXJlbnRfd2FzX2luX2NvbGxlZ2UgfCBXaGV0aGVyIHRoZSBzdHVkZW50J3MgcGFyZW50cyBhdHRlbmRlZCBjb2xsZWdlLiAgICAgICAgICAgIHwgQmluYXJ5ICAgIHwgVHJ1ZSAtIEZhbHNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgd2lsbF9nb190b19jb2xsZWdlICAgIHwgV2hldGhlciB0aGUgc3R1ZGVudCBwbGFucyB0byBnbyB0byBjb2xsZWdlLiAgICAgICAgICAgICAgICB8IEJpbmFyeSAgICB8IFRydWUgLSBGYWxzZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCiMjIFNhbXBsZSBvZiBvdXIgZGF0YQ0KDQpgYGB7cn0NCmhlYWQoRGF0YXNldCkNCmBgYA0KDQpIZXJlIGFyZSBhIHNhbXBsZSBvZiA2IHJvdyBmcm9tIG91ciBkYXRhc2V0Lg0KDQojIyBNaXNzaW5nIHZhbHVlcw0KDQpgYGB7cn0NCnN1bShpcy5uYShEYXRhc2V0KSkNCmBgYA0KDQpUaGVyZSBhcmUgbm8gbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuDQoNCiMjIFN0YXRpc3RpY2FsIGdyYXBocw0KDQpHcmFwaCAxOg0KDQpgYGB7cn0NCmRmID1kYXRhLmZyYW1lKERhdGFzZXQpDQpnZ3Bsb3QoZGF0YT1kZiwgYWVzKHggPSBpbnRlcmVzdCwgZmlsbCA9IHdpbGxfZ29fdG9fY29sbGVnZSkpICsNCiAgZ2VvbV9iYXIoKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygnTm90IEludGVyZXN0ZWQnLCAnTGVzcyBJbnRlcmVzdGVkJywgJ1VuY2VydGFpbicsICdJbnRlcmVzdGVkJywgJ1ZlcnkgSW50ZXJlc3RlZCcpKSArDQogIGxhYnModGl0bGUgPSAnQ29sbGVnZSBpbnRlcmVzdCB2cyBDb2xsZWdlIGF0dGVuZGFuY2UgJykgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJUcnVlIiA9ICJhbnRpcXVld2hpdGUyIiwgIkZhbHNlIiA9ICJhbnRpcXVld2hpdGUzIikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCg0KYGBgDQoNCkFjY29yZGluZyB0byB0aGUgZ3JhcGgsIHdoZXRoZXIgc3R1ZGVudHMgYXJlIGludGVyZXN0ZWQgaW4gZ29pbmcgdG8gY29sbGVnZSBvciBub3QgZG9lcyBub3QgYWZmZWN0IHdoZXRoZXIgdGhleSBhY3R1YWxseSBlbmQgdXAgYXR0ZW5kaW5nIENvbGxhZ2UgLiBUaGVyZSBpcyBhIGdyb3VwIG9mIGluZGl2aWR1YWxzIHdobyB3ZXJlIGludGVyZXN0ZWQgaW4gYXR0ZW5kaW5nIGJ1dCBkaWQgbm90IHJlY2VpdmUgYWNjZXB0YW5jZSwgd2hpbGUgb3RoZXJzIHdobyB3ZXJlIG5vdCBpbnRlcmVzdGVkIHdlcmUgYWNjZXB0ZWQuDQoNCkdyYXBoIDI6DQoNCmBgYHtyfQ0KDQpmaWx0ZXJlZF9UcnVlID1maWx0ZXIoRGF0YXNldCwgd2lsbF9nb190b19jb2xsZWdlID09ICdUcnVlJykNCmZpbHRlcmVkX0ZhbHNlID1zdWJzZXQoRGF0YXNldCwgd2lsbF9nb190b19jb2xsZWdlID09J0ZhbHNlJykNCg0KZ2dwbG90KCkgKw0KICBnZW9tX2RlbnNpdHkoZGF0YSA9IGZpbHRlcmVkX1RydWUsIGFlcyh4ID0gYXZlcmFnZV9ncmFkZXMsIGZpbGwgPSAiR29pbmcgdG8gQ29sbGVnZSIpLCBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX2RlbnNpdHkoZGF0YSA9IGZpbHRlcmVkX0ZhbHNlLCBhZXMoeCA9IGF2ZXJhZ2VfZ3JhZGVzLCBmaWxsID0gIk5PVCBHb2luZyB0byBDb2xsZWdlIiksIGFscGhhID0gMC41KSArDQogIGxhYnMoeCA9ICJBdmVyYWdlIEdyYWRlcyIsIHkgPSAiRGVuc2l0eSIpICsNCiAgZ2d0aXRsZSgiQ29tcGFyaXNvbiBvZiBBdmVyYWdlIEdyYWRlcyBmb3IgU3R1ZGVudHMgR29pbmcgdG8gQ29sbGVnZSBhbmQgTk9UIEdvaW5nIHRvIENvbGxlZ2UiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkdvaW5nIHRvIENvbGxlZ2UiID0gImFudGlxdWV3aGl0ZTQiLCAiTk9UIEdvaW5nIHRvIENvbGxlZ2UiID0gImFudGlxdWV3aGl0ZTEiKSkNCg0KYGBgDQoNClRoZSBncmFwaCBzaG93cyB0aGF0IHRoZSBhdmVyYWdlIGdyYWRlcyBmb3Igc3R1ZGVudHMgd2hvIGhhZCBhY2NlcHRlZCB0byBnbyB0byBjb2xsZWdlIHdlcmUgaGlnaGVyIHRoYW4gdGhvc2Ugd2hvIGRpZCBub3QgZW50ZXIgY29sbGVnZSAsIGFuZCB0aGlzIGluZGljYXRlcyB0aGUgZXhpc3RlbmNlIG9mIGEgY29ycmVsYXRpb24gYmV0d2VlbiB0aG9zZSB3aG8gZ29pbmcgdG8gY29sbGVnZSBhbmQgdGhlIGF2ZXJhZ2UgZ3JhZGVzDQoNCkdyYXBoIDM6DQoNCmBgYHtyfQ0KRGF0YXNldF9wZXJjZW50YWdlIDwtIERhdGFzZXQgJT4lDQogIGdyb3VwX2J5KHR5cGVfc2Nob29sKSAlPiUNCiAgc3VtbWFyaXNlKHBlcmNlbnRhZ2UgPSBtZWFuKHdpbGxfZ29fdG9fY29sbGVnZSA9PSAiVHJ1ZSIpICogMTAwKQ0KDQojIENyZWF0ZSBhIHBlcmNlbnRhZ2UgY2hhcnQNCmdncGxvdChEYXRhc2V0X3BlcmNlbnRhZ2UsIGFlcyh4ID0gdHlwZV9zY2hvb2wsIHkgPSBwZXJjZW50YWdlLCBmaWxsID0gdHlwZV9zY2hvb2wpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiUGVyY2VudGFnZSBvZiBTdHVkZW50cyBHb2luZyB0byBDb2xsZWdlIGJ5IFR5cGUgb2YgU2Nob29sIiwNCiAgICAgICB4ID0gIlR5cGUgb2YgU2Nob29sIiwNCiAgICAgICB5ID0gIlBlcmNlbnRhZ2UiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkFjYWRlbWljIiA9ICJhbnRpcXVld2hpdGUzIiwgIlZvY2F0aW9uYWwiID0gImFudGlxdWV3aGl0ZTIiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KIA0KYGBgDQoNClRoaXMgZ3JhcGggc2hvd3MgdGhlIGltcGFjdCBvZiB0aGUgdHlwZSBvZiBoaWdoIHNjaG9vbCBhdHRlbmRlZCBieSBzdHVkZW50cyBvbiB0aGVpciBjb2xsZWdlIGF0dGVuZGFuY2UgLiBCYXNlZCBvbiB0aGUgYmFyIGNoYXJ0Og0KDQotICAgYW1vbmcgc3R1ZGVudHMgZnJvbSBBY2FkZW1pYyBoaWdoIHNjaG9vbHMsIDMxMyBhcmUgZ29pbmcgdG8gY29sbGVnZSwgYW5kIDI5NiBhcmUgbm90Lg0KDQotICAgYW1vbmcgc3R1ZGVudHMgZnJvbSBWb2NhdGlvbmFsIGhpZ2ggc2Nob29scywgMTg3IGFyZSBnb2luZyB0byBjb2xsZWdlLCBhbmQgMjA0IGFyZSBub3QNCg0KVGhlc2UgaW5mb3JtYXRpb24gdGVsbCB1cyB0aGF0IGEgaGlnaGVyIHByb3BvcnRpb24gb2Ygc3R1ZGVudHMgZnJvbSBhY2FkZW1pYyBoaWdoIHNjaG9vbHMgYXJlIGdvaW5nIHRvIGNvbGxlZ2UgY29tcGFyZWQgdG8gdGhvc2UgZnJvbSB2b2NhdGlvbmFsIHNjaG9vbHMgd2hpY2ggc3VnZ2VzdCB0aGF0IHRoZSB0eXBlIG9mIHNjaG9vbCBhdHRlbmRlZC4NCg0KR3JhcGggNDoNCg0KYGBge3J9DQpnZ3Bsb3QoRGF0YXNldCwgYWVzKHggPSBhdmVyYWdlX2dyYWRlcykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gImFudGlxdWV3aGl0ZTIiLCBjb2xvciA9ICJhbnRpcXVld2hpdGU0IikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBzdHVkZW50cycgZ3JhZGVzIiwNCiAgICAgICB4ID0gIlN0dWRlbnRzJyBhdmVyYWdlIGdyYWRlcyIsDQogICAgICAgeSA9ICJGcmVxdWVuY3kiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNClRoaXMgaGlzdG9ncmFtIHNob3cgdXMgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdGhlIHN0dWRlbnRzIGluIHRoZSBkYXRhc2V0IGFyZSBwZXJmb3JtaW5nIHdlbGwgc2luY2UgaXQgc2VlbXMgbGlrZSB0aGVpciBncmFkZXMgYXJlIHNwYW5uaW5nIGJldHdlZW4gNzUgYW5kIDk4LiBUaGlzIGFuYWx5c2lzIHdpbGwgaGVscCB1cyBkZXRlcm1pbmUgd2hldGhlciB0aGUgYWNhZGVtaWMgcGVyZm9ybWFuY2UgbGV2ZWwgb2Ygc3R1ZGVudHMgaXMgYSBjb250cmlidXRpbmcgZmFjdG9yIHRvIHRoZWlyIGNvbGxlZ2UgYXR0ZW5kYW5jZSBvciBub3QuDQoNCiMjIFN0YXRpc3RpY2FsIE1lYXN1cmVzDQoNCjEuICBUaGUgc3R1ZGVudCdzIGFjYWRlbWljIHBlcmZvcm1hbmNlIGFuYWx5c2lzOg0KDQpgYGB7cn0NCnN1bW1hcnkoRGF0YXNldCRhdmVyYWdlX2dyYWRlcykNCg0KYGBgDQoNClRoZSBzdHVkZW50IGdyYWRlcyBpbiBvdXIgZGF0YXNldCByYW5nZSBmcm9tIDc1LjAwIHRvIDk4LjAwLCB3aXRoIGEgbWVkaWFuIG9mIDg1LjU4IGFuZCBhbiBhdmVyYWdlIG9mIDg2LjEwLiBUaGlzIHN1Z2dlc3RzIHRoYXQgbW9zdCBzdHVkZW50cyBhcmUgZG9pbmcgd2VsbCBhcyBub25lIG9mIHRoZW0gaGF2ZSBhdmVyYWdlIGdyYWRlcyBiZWxvdyA1MC4gSG93ZXZlciwgaXQncyBpbnRlcmVzdGluZyB0byBub3RlIHRoYXQgc29tZSBzdHVkZW50cyBoYXZlIG11Y2ggaGlnaGVyIG9yIGxvd2VyIGdyYWRlcyB0aGFuIHRoZSBhdmVyYWdlLCBtYWlubHkgZHVlIHRvIHRoZSB3aWRlIHJhbmdlIG9mIGdyYWRlcy4uDQoNCjIuICBUaGUgc29jaW9lY29ub21pYyBzdGF0dXMgb2Ygc3R1ZGVudHMnIGZhbWlsaWVzIGFuYWx5c2lzOg0KDQpgYGB7cn0NCnN1bW1hcnkoRGF0YXNldCRwYXJlbnRfc2FsYXJ5KQ0KYGBgDQoNCkluIHRoZSBkYXRhc2V0LCB3ZSd2ZSBnb3Qgc3R1ZGVudHMgcGFyZW50cyB3aXRoIHNhbGFyaWVzIHJhbmdpbmcgZnJvbSAxLDAwMCwwMDAgdG8gMTAsMDAwLDAwMCBJRFIvUnVwaWFoLiBUaGUgbWVkaWFuIHNhbGFyeSBpcyA1LDQ0MCwwMDAgSURSL1J1cGlhaCwgYW5kIHRoZSBhdmVyYWdlIGlzIDUsMzgxLDU3MCBJRFIvUnVwaWFoLiBUaGlzIGRhdGEgdGVsbHMgdXMgdGhhdCBtYW55IHBhcmVudHMgaW4gb3VyIGRhdGFzZXQgZWFybiBsZXNzIHRoYW4gdGhlIGF2ZXJhZ2Ugc2FsYXJ5IGluIEluZG9uZXNpYSwgd2hpY2ggaXMgMTQ2LDAwMCwwMDAgSURSLiBUaGlzIHN1Z2dlc3RzIHRoYXQgcXVpdGUgYSBmZXcgc3R1ZGVudHMgaW4gb3VyIGRhdGFzZXQgY29tZSBmcm9tIGZhbWlsaWVzIHdpdGggbGltaXRlZCBmaW5hbmNlcy4gQW5kIHRoaXMgZmluYW5jaWFsIHNpdHVhdGlvbiBjb3VsZCBjZXJ0YWlubHkgaW1wYWN0IHRoZWlyIGFiaWxpdHkgdG8gZ2V0IHRocm91Z2ggY29sbGVnZS4NCg0KYGBge3J9DQpzdW1tYXJ5KERhdGFzZXQkaG91c2VfYXJlYSkNCmBgYA0KDQpBZGRpdGlvbmFsbHkgd2UgY2FuIHV0aWxpemUgdGhlIGhvdXNlIGFyZWEgYXR0cmlidXRlIHRvIGdhaW4gYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgc29jaW9lY29ub21pYyBzdGF0dXMgb2Ygc3R1ZGVudHMnIGZhbWlsaWVzLCB3aGVyZSBzdHVkZW50cyB3aXRoIGhvdXNlcyBzaWduaWZpY2FudGx5IGxhcmdlciB0aGFuIHRoZSBtZWFuIG1pZ2h0IGluZGljYXRlIGEgaGlnaGVyIHNvY2lvZWNvbm9taWMgc3RhdHVzLCB3aGlsZSB0aG9zZSB3aXRoIGhvdXNlcyBjb25zaWRlcmFibHkgc21hbGxlciB0aGFuIHRoZSBtZWFuIG1pZ2h0IHJlZmxlY3QgYSBjb21wYXJhdGl2ZWx5IGxvd2VyIHNvY2lvZWNvbm9taWMgc3RhdHVzLiBCYXNlZCBvbiB0aGUgc2hvd24gb3V0cHV0LCB0aGUgaG91c2UgYXJlYXMgcmFuZ2UgZnJvbSBbMjAuMDAtMTIwLjAwIOOOoV0uIFRoZSBtZWRpYW4gaG91c2UgYXJlYSBpcyA3NS41MCDjjqEgaW5kaWNhdGVzIHRoYXQgZmFtaWxpZXMgd2l0aCBob3VzZSBhcmVhcyBhcm91bmQgdGhpcyB2YWx1ZSBsaWtlbHkgaGF2ZSBtb2RlcmF0ZSBzb2Npb2Vjb25vbWljIHN0YXR1cyB3aXRoIGhvdXNlcyB0aGF0IG5laXRoZXIgdmVyeSBzbWFsbCBub3IgdmVyeSBsYXJnZS4NCg0KMy4gIFVuZGVyc3RhbmRpbmcgUGFyZW50IEFnZSBSYW5nZSBhbmQgVmFyaWF0aW9uIGluIGl0cyBWYWx1ZXMNCg0KYGBge3J9DQpzdW1tYXJ5KERhdGFzZXQkcGFyZW50X2FnZSkNClNEPXNkKERhdGFzZXQkcGFyZW50X2FnZSkNCk1lYW5BZ2U9bWVhbihEYXRhc2V0JHBhcmVudF9hZ2UpDQpjYXQoImNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbjoiLFNEL01lYW5BZ2UqMTAwLCIlIikNCg0KYGBgDQoNClRoaXMgc3VtbWFyeSBwcm92aWRlcyB0aGUgcmFuZ2UgZm9yIGFnZSBhdHRyaWJ1dGUgWzQwLDY1XSB3aGljaCBpbmRpY2F0ZXMgdGhhdCBhbGwgcGFyZW50IGluIG1pZGRsZSBhZ2UgZHVyaW5nIHRoaXMgYWdlIHBhcmVudCBoYXZlIG1vcmUgY29uY2VybiBhYm91dCB0aGVpciBjaGlsZHJlbiAsIHRoZSBjb2VmZmljaWVudCBvZiB2YXJpYXRpb249IDYuNyUgd2hpY2ggaW5kaWNhdGVzIGxvd2VyIHZhcmlhdGlvbiAsYW5kIHRoZSB2YWx1ZSBvZiBhdHRyaWJ1dGUgcGFyZW50X2FnZXJhcmUgYXJlIHJlbGF0aXZlbHkgY2xvc2UgdG8gdGhlIG1lYW4gb3ZlcmFsbCAyNSUgb2YgdGhlbSBoYXZlIGFuIGFnZSBiZWxvdyBvciBlcXVhbCB0byA1MCAsIDc1JSBoYXZlIGFuIGFnZSBiZWxvdyBvciBlcXVhbCB0byA1NCBhbmQgdGhlIG1lZGlhbiB2YWx1ZSBpcyA1Mg0KDQojIyBQcmVwcm9jZXNzaW5nIGRhdGENCg0Kd2UgYWltIHRvIGluY3JlYXNlIHRoZSBxdWFsaXR5IG9mIGRhdGEgYW5kIHRyYW5zZm9ybWluZyB0aGUgZGF0YSB0byBtYWtlIGl0IHN1aXRhYmxlIGZvciBhbmFseXNpcw0KDQojIyBPdXRsaWVycyBhbmFseXNpcw0KDQpgYGB7cn0NCiMjI3BhcmVudCBhZ2Ugb3V0bGllcnMNCnF1YXJ0aWxlcyA8LSBxdWFudGlsZShEYXRhc2V0JHBhcmVudF9hZ2UsIHByb2JzPWMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQ0KSVFSIDwtIElRUihEYXRhc2V0JHBhcmVudF9hZ2UpDQoNCkxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSpJUVINClVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSpJUVIgDQoNCmRhdGFfbm9fb3V0bGllciA8LSBzdWJzZXQoRGF0YXNldCwgRGF0YXNldCRwYXJlbnRfYWdlID4gTG93ZXIgJiBEYXRhc2V0JHBhcmVudF9hZ2UgPCBVcHBlcikNCmRpbShkYXRhX25vX291dGxpZXIpDQoNCiMjI3BhcmVudCBzYWxhcnkgb3V0bGllcnMNCnF1YXJ0aWxlcyA8LSBxdWFudGlsZShEYXRhc2V0JHBhcmVudF9zYWxhcnksIHByb2JzPWMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQ0KSVFSIDwtIElRUihEYXRhc2V0JHBhcmVudF9zYWxhcnkpDQoNCkxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSpJUVINClVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSpJUVIgDQoNCmRhdGFfbm9fb3V0bGllciA8LSBzdWJzZXQoZGF0YV9ub19vdXRsaWVyLCBkYXRhX25vX291dGxpZXIkcGFyZW50X3NhbGFyeT4gTG93ZXIgJiBkYXRhX25vX291dGxpZXIkcGFyZW50X3NhbGFyeSA8IFVwcGVyKQ0KZGltKGRhdGFfbm9fb3V0bGllcikNCg0KDQojIyNhdmVyZ2UgZ3JhZGVzIG91dGxpZXJzDQpxdWFydGlsZXMgPC0gcXVhbnRpbGUoRGF0YXNldCRhdmVyYWdlX2dyYWRlcywgcHJvYnM9YyguMjUsIC43NSksIG5hLnJtID0gRkFMU0UpDQpJUVIgPC0gSVFSKERhdGFzZXQkYXZlcmFnZV9ncmFkZXMpDQoNCkxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSpJUVINClVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSpJUVIgDQoNCmRhdGFfbm9fb3V0bGllciA8LSBzdWJzZXQoZGF0YV9ub19vdXRsaWVyLCBkYXRhX25vX291dGxpZXIkYXZlcmFnZV9ncmFkZXM+IExvd2VyICYgZGF0YV9ub19vdXRsaWVyJGF2ZXJhZ2VfZ3JhZGVzIDwgVXBwZXIpDQpkaW0oZGF0YV9ub19vdXRsaWVyKQ0KDQoNCiMjI2hvdXNlIGFyZWEgb3V0bGllcnMNCnF1YXJ0aWxlcyA8LSBxdWFudGlsZShEYXRhc2V0JGhvdXNlX2FyZWEsIHByb2JzPWMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQ0KSVFSIDwtIElRUihEYXRhc2V0JGhvdXNlX2FyZWEpDQoNCkxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSpJUVINClVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSpJUVIgDQoNCmRhdGFfbm9fb3V0bGllciA8LSBzdWJzZXQoZGF0YV9ub19vdXRsaWVyLCBkYXRhX25vX291dGxpZXIkaG91c2VfYXJlYT4gTG93ZXIgJiBkYXRhX25vX291dGxpZXIkaG91c2VfYXJlYSA8IFVwcGVyKQ0KDQpGb3VuZGVkX091dGxpZXJzPWRhdGEuZnJhbWUoYW50aV9qb2luKERhdGFzZXQsZGF0YV9ub19vdXRsaWVyKSkNCnByaW50KEZvdW5kZWRfT3V0bGllcnMpDQpgYGANCg0KQWZ0ZXIgY29uZHVjdGluZyBkYXRhIGFuYWx5c2lzIGFuZCBpZGVudGlmeWluZyBvdXRsaWVycywgb3VyIGluc3BlY3Rpb24gcmV2ZWFscyB0aGF0IHRoZSBkZXRlY3RlZCBvdXRsaWVycyByZXByZXNlbnQgaW5oZXJlbnQgdmFyaWF0aW9uIHdpdGhpbiB0aGUgcG9wdWxhdGlvbi4gUmVnYXJkaW5nIFBhcmVudF9hZ2UsIG91dGxpZXJzIGFyZSBvYnNlcnZlZCBmb3IgdmFsdWVzIGJlbG93IDQ0IGFuZCBhYm92ZSA2NS4gSG93ZXZlciwgaXQgc2hvdWxkIGJlIG5vdGVkIHRoZSBhZ2UgZnJvbSA0MCB0byA2NSBmYWxsIHdpdGhpbiB0aGUgZXhwZWN0ZWQgbWVhbiBvZiBvdXIgZGF0YXNldCBtZWFuaW5nIHRoYXQgaXQgZG9lc24ndCBpbmRpY2F0ZSB0aGF0IHRoZXkgYXJlIG91dGxpZXJzIC4gRm9yIHBhcmVudF9zYWxhcnksIHdlIGZvdW5kIHR3byBvdXRsaWVyczogb25lIGJlbG93IDEsMzI2LDI1MCBpbmQg4omIIDg1IFVTRCBhbmQgYW5vdGhlciBhYm92ZSA5LDQxNiwyNTAgaW5kIOKJiCA2MDYgVVNELiBUaGUgbWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZXMgd2VyZSBkZXRlcm1pbmVkIHRvIGJlIDEsMDAwLDAwMCBpbmQg4omIIDY0IFVTRCBhbmQgMTAsMDAwLDAwMCBpbmQg4omIIDY0NCBVU0QsIHJlc3BlY3RpdmVseS4gSW4gdGhlIGNhc2Ugb2YgZ3JhZGVzLCB0d2VsdmUgb3V0bGllcnMgd2VyZSBpZGVudGlmaWVkLCByYW5naW5nIGZyb20gYmVsb3cgNzYgdG8gYWJvdmUgOTcuIE5ldmVydGhlbGVzcywgc2luY2UgdGhlIGRhdGEgZmFsbHMgd2l0aGluIHRoZSBhY2NlcHRhYmxlIHJhbmdlIG9mIDAgdG8gMTAwLCB0aGVzZSBvdXRsaWVycyBzaG91bGQgYmUgcmV0YWluZWQgYXMgdGhleSBhcmUgc3RpbGwgY29uc2lkZXJlZCBub3JtYWwgYW5kIHdpdGhpbiB0aGUgdXN1YWwgZ3JhZGUgcmFuZ2UuIEZpbmFsbHksIGZvciBob3VzZV9hcmVhLCB3ZSBmb3VuZCBlbGV2ZW4gb3V0bGllcnMgYmVsb3cgMzQuNG0gYW5kIGFib3ZlIDExNW0sIHdpdGggdGhlIG1pbmltdW0gYmVpbmcgMjBtIGFuZCB0aGUgbWF4aW11bSBiZWluZyAxMjBtLiBIb3dldmVyLCB0aGVzZSB2YWx1ZXMgYXJlIHN0aWxsIGNvbnNpZGVyZWQgdHlwaWNhbCBmb3IgdGhlIHBvcHVsYXRpb24uDQoNCiMjIE5vcm1hbGl6YXRpb24NCg0KYGBge3J9DQpub3JtYWxpemUgPC0gZnVuY3Rpb24oeCkge3JldHVybigoeC1taW4oeCkpLyAobWF4KHgpLW1pbih4KSkpfQ0KZGF0YXNldFdpdGhvdXROb3JtYWxpemF0aW9uPC1EYXRhc2V0DQpEYXRhc2V0JHBhcmVudF9zYWxhcnk8LW5vcm1hbGl6ZShkYXRhc2V0V2l0aG91dE5vcm1hbGl6YXRpb24kcGFyZW50X3NhbGFyeSkNCkRhdGFzZXQkaG91c2VfYXJlYTwtbm9ybWFsaXplKGRhdGFzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiRob3VzZV9hcmVhKQ0KcHJpbnQoRGF0YXNldCkNCmBgYA0KDQpXZSBhcHBsaWVkIG5vcm1hbGl6YXRpb24gdG8gdGhlICdwYXJlbnRfc2FsYXJ5JyBhbmQgJ2hvdXNlX2FyZWEnIGF0dHJpYnV0ZXMsIHNjYWxpbmcgdGhlaXIgdmFsdWVzIHRvIGEgcmFuZ2UgYmV0d2VlbiAwIGFuZCAxLiBUaGlzIG5vcm1hbGl6YXRpb24gcHJvY2VzcyBncmVhdGx5IGZhY2lsaXRhdGVzIGRhdGEgaGFuZGxpbmcgYW5kIGFuYWx5c2lzLCBlbnN1cmluZyB0aGF0IHRoZXNlIGF0dHJpYnV0ZXMgYXJlIG9uIGEgY29uc2lzdGVudCBzY2FsZS4gV2hpY2ggd2lsbCBpbXByb3ZlIHRoZSByZWxpYWJpbGl0eSBvZiBvdXIgZGF0YSBhbmFseXNpcyBhbmQgZW5hYmxlIGJldHRlciBjb25jbHVzaW9ucyB0byBiZSBkcmF3biBmcm9tIHRoZSBkYXRhc2V0LiBOb3JtYWxpemF0aW9uIGlzIGEgY3J1Y2lhbCBzdGVwIGluIHByZXBhcmluZyB0aGUgZGF0YSBmb3IgbW9kZWxpbmcsIGFzIGl0IHByZXZlbnRzIGF0dHJpYnV0ZXMgd2l0aCBsYXJnZXIgbnVtZXJpY2FsIHJhbmdlcyBmcm9tIGRvbWluYXRpbmcgdGhlIGFuYWx5c2lzIGFuZCBlbnN1cmVzIGZhaXIgdHJlYXRtZW50IGZvciBhbGwgZmVhdHVyZXMuDQoNCiMjIERpc2NyZXRpemF0aW9uDQoNCmBgYHtyfQ0KDQoNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgW0RhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPj0gOTVdIDwtICcrQScNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzk1ID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA5MF0gPC0gJ0EnDQpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIFs5MCA+RGF0YXNldCRhdmVyYWdlX2dyYWRlcyAmIERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPj0gODVdIDwtICcrQicNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzg1ID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA4MF0gPC0gJ0InDQpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIFs4MCA+RGF0YXNldCRhdmVyYWdlX2dyYWRlcyAmIERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPj0gNzVdIDwtICcrQycNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzc1ID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA3MF0gPC0gJ0MnDQpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIFs3MCA+RGF0YXNldCRhdmVyYWdlX2dyYWRlcyAmIERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPj0gNjVdIDwtICcrRCcNCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzY1ID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA2MF0gPC0gJ0QnDQpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIFs2MCA+RGF0YXNldCRhdmVyYWdlX2dyYWRlcyAmIERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPj0gMF0gPC0gJ0YnDQpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIDwtIGFzLmNoYXJhY3RlcihEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICkNCnByaW50KERhdGFzZXQpDQpgYGANCg0KV2UgdHJhbnNmb3JtZWQgdGhlIHBhcmVudF9hZ2UgYXR0cmlidXRlIGludG8gaW50ZXJ2YWxzIGJ5IGRpdmlkaW5nIHRoZSB2YWx1ZXMgdG8gYmUgZmFsbCBvbiBvbmUgb2YgdHdvIHBvc3NpYmxlIGludGVydmFsIGxhYmVscyB3aXRoIGVxdWFsIHdpZHRoIHdoaWNoIGlzKDQwLDUwXSwoNTAsNjBdIGJ5IGRpc2NyZXRpemF0aW9uIHRoZSB2YWx1ZXMgd2VsbCBiZSBzaW1wbGVyIHRvIGNsYXNzaWZ5IG9yIHBlcmZvcm0gb3RoZXIgbWV0aG9kcyB0aGF0IGNhbiBoZWxwIHVzIGxhdGVyIGluIG91ciBtb2RlbC4NCg0KYW5kIHRvIGJldHRlciB1dGlsaXplIGFuZCBpbnRlcnByZXQgdGhlIGdyYWRlcyBhdHRyaWJ1dGVzIGZvciBlYWNoIHN0dWRlbnQsIHdlIGhhdmUgY29udmVydGVkIHRoZSBudW1lcmljIGdyYWRlcyBpbnRvIGxldHRlciBncmFkZXMgKEErLCBBLCBCKywgQiwgQyssIEMsIEQrLCBELCBGKS4gVGhpcyB0cmFuc2Zvcm1hdGlvbiB3YXMgdW5kZXJ0YWtlbiB0byBmb2N1cyBvbiB0aGUgZ2VuZXJhbCBsZXR0ZXIgZ3JhZGUgcmVwcmVzZW50YXRpb24gcmF0aGVyIHRoYW4gdGhlIHByZWNpc2UgbnVtZXJpY2FsIHZhbHVlcy4NCg0KIyMgRW5jb2RpbmcNCg0KYGBge3J9DQpEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZVtEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZT09IlRSVUUiXTwtMQ0KRGF0YXNldCRwYXJlbnRfd2FzX2luX2NvbGxlZ2VbRGF0YXNldCRwYXJlbnRfd2FzX2luX2NvbGxlZ2U9PSJUcnVlIl08LTENCkRhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlW0RhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlPT0iRkFMU0UiXTwtMA0KRGF0YXNldCRwYXJlbnRfd2FzX2luX2NvbGxlZ2VbRGF0YXNldCRwYXJlbnRfd2FzX2luX2NvbGxlZ2U9PSJGYWxzZSJdPC0wDQoNCkRhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlW0RhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlPT0iVFJVRSJdPC0wDQpEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZVtEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZT09IlRydWUiXTwtMA0KRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2VbRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2U9PSJGQUxTRSJdPC0xDQpEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZVtEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZT09IkZhbHNlIl08LTENCg0KRGF0YXNldCRnZW5kZXJbRGF0YXNldCRnZW5kZXI9PSJGZW1hbGUiXTwtMQ0KRGF0YXNldCRnZW5kZXJbRGF0YXNldCRnZW5kZXI9PSJNYWxlIl08LTANCg0KRGF0YXNldCRzY2hvb2xfYWNjcmVkaXRhdGlvbltEYXRhc2V0JHNjaG9vbF9hY2NyZWRpdGF0aW9uPT0iQSJdPC0xDQpEYXRhc2V0JHNjaG9vbF9hY2NyZWRpdGF0aW9uW0RhdGFzZXQkc2Nob29sX2FjY3JlZGl0YXRpb249PSJCIl08LTANCg0KRGF0YXNldCRpbnRlcmVzdFtEYXRhc2V0JGludGVyZXN0PT0iVmVyeSBJbnRlcmVzdGVkIl08LTQNCkRhdGFzZXQkaW50ZXJlc3RbRGF0YXNldCRpbnRlcmVzdD09IkludGVyZXN0ZWQiXTwtMw0KRGF0YXNldCRpbnRlcmVzdFtEYXRhc2V0JGludGVyZXN0PT0iTGVzcyBJbnRlcmVzdGVkIl08LTINCkRhdGFzZXQkaW50ZXJlc3RbRGF0YXNldCRpbnRlcmVzdD09Ik5vdCBJbnRlcmVzdGVkIl08LTENCkRhdGFzZXQkaW50ZXJlc3RbRGF0YXNldCRpbnRlcmVzdD09IlVuY2VydGFpbiJdPC0wDQoNCg0KRGF0YXNldCR0eXBlX3NjaG9vbFtEYXRhc2V0JHR5cGVfc2Nob29sPT0iQWNhZGVtaWMiXTwtMSANCkRhdGFzZXQkdHlwZV9zY2hvb2xbRGF0YXNldCR0eXBlX3NjaG9vbD09IlZvY2F0aW9uYWwiXTwtMA0KDQpEYXRhc2V0JHJlc2lkZW5jZVtEYXRhc2V0JHJlc2lkZW5jZT09IlVyYmFuIl08LTENCkRhdGFzZXQkcmVzaWRlbmNlW0RhdGFzZXQkcmVzaWRlbmNlPT0iUnVyYWwiXTwtMA0KcHJpbnQoRGF0YXNldCkNCmBgYA0KDQpTaW5jZSBlbmNvZGluZyBpcyBhbiBpbXBvcnRhbnQgc3RlcCBpbiBkYXRhIHByZXByb2Nlc3NpbmcgdGhhdCBlbmFibGVzIHRoZSB1c2Ugb2YgY2F0ZWdvcmljYWwgZGF0YSBpbiB2YXJpb3VzIGRhdGEgYW5hbHlzaXMgYW5kIG1hY2hpbmUgbGVhcm5pbmcgdGFza3MsIHdlIGVuY29kZWQgYXR0cmlidXRlcyBsaWtlIHRoZSAncGFyZW50IHdhcyBpbiBjb2xsZWdlJyBhdHRyaWJ1dGUgZnJvbSAoVHJ1ZSwgRmFsc2UpIHRvICgxLCAwKSwgYW5kICd3aWxsIGdvIHRvIGNvbGxlZ2UnIGZyb20gKFRydWUsIEZhbHNlKSB0byAoMCwgMSkuIFRoaXMgZW5jb2RpbmcgaXMgY2FycmllZCBvdXQgYXMgd2UgYWltIHRvIHByZWRpY3QgdGhlIGluZmx1ZW5jaW5nIGZhY3RvcnMuIEFkZGl0aW9uYWxseSwgd2UgZW5jb2RlZCB0aGUgJ2dlbmRlcicgYXR0cmlidXRlIGZyb20gKEZlbWFsZSwgTWFsZSkgdG8gKDEsIDApLCAnc2Nob29sIGFjY3JlZGl0YXRpb24nIGZyb20gKEEsIEIpIHRvICgxLCAwKSwgJ3R5cGVfc2Nob29sJyBmcm9tIChBY2FkZW1pYywgVm9jYXRpb25hbCkgdG8gKDEsIDApLCAncmVzaWRlbmNlJyBmcm9tIChVcmJhbiwgUnVyYWwpIHRvICgxLCAwKSwgYW5kICdpbnRlcmVzdCcgZnJvbSAoVmVyeSBpbnRlcmVzdGVkICxJbnRlcmVzdGVkICwgTGVzcyBJbnRlcmVzdGVkICwgTm90IEludGVyZXN0ZWQgLFVuY2VydGFpbiApIHRvICg0LDMsMiwgMSwgMCkgcmVzcGVjdGl2ZWx5LiBFbmNvZGluZyBzZXJ2ZXMgdG8gc2ltcGxpZnkgdGhlIGRhdGEsIHJlZHVjZSBjb21wbGV4aXR5LCBhbmQgZW5oYW5jZSBpdHMgc3VpdGFiaWxpdHkgZm9yIG1vZGVsaW5nIHB1cnBvc2VzLg0KDQojIyBDb3JyZWxhdGlvbiBhbmFseXNpcyBDaGkgc3F1YXJlIHRlc3QgZm9yIG5vbWluYWwgYXR0cmlidXRlOg0KDQpgYGB7cn0NCg0KIzENCkM9Y2hpc3EudGVzdChEYXRhc2V0JHR5cGVfc2Nob29sICwgRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpwcmludChDKQ0KIzINCg0KQz1jaGlzcS50ZXN0KERhdGFzZXQkc2Nob29sX2FjY3JlZGl0YXRpb24gLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkNCnByaW50KEMpDQojMw0KQz1jaGlzcS50ZXN0KERhdGFzZXQkZ2VuZGVyICwgRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpwcmludChDKQ0KIzQNCkM9Y2hpc3EudGVzdChEYXRhc2V0JGludGVyZXN0ICwgRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpwcmludChDKQ0KDQojNQ0KQz1jaGlzcS50ZXN0KERhdGFzZXQkcmVzaWRlbmNlICwgRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2UpDQpwcmludChDKQ0KDQojNg0KQz1jaGlzcS50ZXN0KERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkNCnByaW50KEMpDQoNCiM3DQpDPWNoaXNxLnRlc3QoRGF0YXNldCRwYXJlbnRfd2FzX2luX2NvbGxlZ2UgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkNCnByaW50KEMpDQpgYGANCg0KQWxsIHRoZSBhdHRyaWJ1dGVzIGhhdmUgWC1zcXVhcmUgZ3JlYXRlciB0aGFuIHRoZSBwLXZhbHVlIHdoaWNoIGluZGljYXRlIGEgc29tZSBhc3NvY2lhdGlvbiB3aXRoIHRoZSBjbGFzcyBsYWJlbDsgdGhlcmVmb3JlIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzDQoNCndlIG5vdGljZWQgZm9yICdpbnRlcmVzdCcgYW5kICdhdmVyYWdlIGdyYWRlJyB0aGUgYW5hbHlzaXMgc2hvd3MgdGhhdCBYLXNxdWFyZSBpcyBtdWNoIGxhcmdlciB0aGFuIHAtdmFsdWUgaW5kaWNhdGUgdGhlIHNpZ25pZmljYW50IGFzc29jaWF0aW9uIG9mIHRoZSB0d28gYXR0cmlidXRlcyB3aXRoIHRoZSBkZWNpc2lvbiBvZiB0aGUgc3R1ZGVudCB0byBnbyB0byB0aGUgY29sbGFnZSBvciBub3QNCg0KIyMgQ29ycmVsYXRpb24gY29lZmZpY2llbnQgYW5hbHlzaXMgZm9yIG51bWVyaWMgYXR0cmlidXRlOg0KDQpgYGB7cn0NCg0KYmlzZXJpYWwuY29yKERhdGFzZXQkcGFyZW50X3NhbGFyeSxEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSwgYygiYWxsLm9icyIsICJjb21wbGV0ZS5vYnMiKSwgbGV2ZWwgPSAxKQ0KYmlzZXJpYWwuY29yKERhdGFzZXQkaG91c2VfYXJlYSxEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSwgYygiYWxsLm9icyIsICJjb21wbGV0ZS5vYnMiKSwgbGV2ZWwgPSAxKQ0KYmlzZXJpYWwuY29yKERhdGFzZXQkcGFyZW50X2FnZSxEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSxjKCJhbGwub2JzIiwgImNvbXBsZXRlLm9icyIpLCBsZXZlbCA9IDEpDQogDQogDQoNCmBgYA0KDQp0aGUgYW5hbHlzaXMgc2hvd3MgbW9kZXJhdGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgZm9yIHBhcmVudCBzYWxhcnkgYW5kIGhvdXNlIGFyZWEgd2l0aCB0aGUgY2xhc3MgbGFiZWwgd2hpY2ggaW5kaWNhdGUgdGhhdCB0aGV5IGFyZSByZWxldmFudCBmYWN0b3JzIG1lYW5pbmcgdGhhdCB0aGUgaGlnaGVyIHRoZSBwYXJlbnQgc2FsYXJ5IGFuZCB0aGUgbGFyZ2VyIGhvdXNlIGFyZWEgdGhlIGhpZ2hlciBwcm9iYWJpbGl0eSBmb3IgYSBzdHVkZW50IHRvIGVucm9sbCBpbiBhIGNvbGxhZ2UNCg0Kd2hlcmUgaXMgdGhlIG9uIG90aGVyIGhhbmQsIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBmb3IgdGhlIHBhcmVudCBhZ2UgaXMgdmVyeSBzbWFsbCB3aGljaCBpbmRpY2F0ZSB0aGF0IHRoZSBwYXJlbnQgYWdlIGhhcyBsaXR0bGUgaW1wYWN0IHRvIHRoZSBwcm9iYWJpbGl0eSBmb3Igc3R1ZGVudCB0byBlbnJvbGwgaW4gYSBjb2xsYWdlDQoNCiMjIEZlYXR1cmUgc2VsZWN0aW9uOg0KDQp1bHRpbWF0ZWx5IGJhc2VkIG9uIHRoZSBhbmFseXNpcyBvZiB0aGUgY29ycmVsYXRpb24gdGhhdCB3ZSBjb25kdWN0ZWQgb24gdGhlIHJlbGF0aW9uc2hpcCBvZiB0aGUgZGF0YXNldCBhdHRyaWJ1dGVzIHdpdGggdGhlIGNsYXNzIGxhYmVsLCBhbmQgdGhlIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRhdGEgYW5kIHRoZSBjb250ZXh0IG9mIGVhY2ggYXR0cmlidXRlIGFuZCBwb3RlbnRpYWwgcmVsZXZhbmNlIHRvIHRoZSBjbGFzcyBsYWJlbCB3ZSBkZWNpZGVkIHRvIG5vdCBkZWxldGUgYW55IG9mIHRoZSBhdHRyaWJ1dGUNCg0KIyMgQ2xhc3NpZmljYXRpb246DQoNClRoZSBhdHRyaWJ1dGUgc2VsZWN0aW9uIG1lYXN1cmUgaXMgYSBoZXVyaXN0aWMgdGVjaG5pcXVlIGVtcGxveWVkIHRvIHNlbGVjdCB0aGUgbW9zdCBvcHRpbWFsIGNyaXRlcmlhIGZvciBkaXZpZGluZyBhIGNvbGxlY3Rpb24gb2YgZGF0YSB0dXBsZXMgaW50byBwYXJ0aXRpb25zIHRoYXQgYXJlIGFzIHB1cmUgYXMgcG9zc2libGUuIFNldmVyYWwgYXR0cmlidXRlIHNlbGVjdGlvbiBtZWFzdXJlcyBhcmUgY29tbW9ubHkgdXRpbGl6ZWQsIHN1Y2ggYXMgSW5mb3JtYXRpb24gR2FpbiAoSUQzKSwgR2FpbiBSYXRpbyAoQzQuNSksIGFuZCBHaW5pIEluZGV4IChDQVJUKS4gd2Ugd2lsbCBjb25zdHJhaW50cyBmb3IgRWFjaCBvZiB0aGVzZSBtZWFzdXJlcyBkaWZmZXJlbnQgcGFydGl0aW9ucyBieSBkaXZpZGluZyB0aGVtIGluIHJhdGlvcyBvZiA1MCUtNTAlLCA3MCUtMzAlLCBhbmQgODAlLTIwJS4NCg0KIyMjIGZhY3RvciB0aGUgZGF0YQ0KDQpgYGB7cn0NCg0KDQpkYXRhPC1QcmVwcm9jZXNzZWRfZGF0YXNldA0KZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UgPC0gYXMuZmFjdG9yKGRhdGEkd2lsbF9nb190b19jb2xsZWdlKQ0KZGF0YSRyZXNpZGVuY2UgPC0gYXMuZmFjdG9yKGRhdGEkcmVzaWRlbmNlKQ0KZGF0YSRnZW5kZXIgPC0gYXMuZmFjdG9yKGRhdGEkZ2VuZGVyKQ0KZGF0YSRwYXJlbnRfd2FzX2luX2NvbGxlZ2UgPC0gYXMuZmFjdG9yKGRhdGEkcGFyZW50X3dhc19pbl9jb2xsZWdlKQ0KZGF0YSRpbnRlcmVzdCA8LSBhcy5mYWN0b3IoZGF0YSRpbnRlcmVzdCkNCmRhdGEkdHlwZV9zY2hvb2wgPC0gYXMuZmFjdG9yKGRhdGEkdHlwZV9zY2hvb2wpDQpkYXRhJHNjaG9vbF9hY2NyZWRpdGF0aW9uIDwtIGFzLmZhY3RvcihkYXRhJHNjaG9vbF9hY2NyZWRpdGF0aW9uKQ0KZGF0YSRhdmVyYWdlX2dyYWRlcyA8LSBhcy5mYWN0b3IoZGF0YSRhdmVyYWdlX2dyYWRlcykNCg0KYGBgDQoNCiMjIyBiYWxhbmNlZCBvciBpbWJhbGFuY2VkDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgDQpsaWJyYXJ5KGNhcmV0KQ0KZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2U8LSBhcy5udW1lcmljKGRhdGEkd2lsbF9nb190b19jb2xsZWdlKQ0KaGlzdChkYXRhJHdpbGxfZ29fdG9fY29sbGVnZSxjb2w9ImNvcmFsIikNCnByb3AudGFibGUodGFibGUoZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpKQ0KYGBgDQoNCndlIHdhbnQgdG8gY29uZmlybSB0aGF0IHRoZSBkaXN0cmlidXRpb24gYmV0d2VlbiB0aGUgdHdvIGxhYmVsIGRhdGEgaXMgbm90IHRvbyBtdWNoIGRpZmZlcmVudC4gQmVjYXVzZSBpbWJhbGFuY2VkIGRhdGFzZXRzIGNhbiBsZWFkIHRvIGltYmFsYW5jZWQgYWNjdXJhY3kuDQoNCkZvcnR1bmF0ZWx5ICxvdXIgZGF0YSBpcyBiYWxhbmNlZA0KDQp8ICoqNzAlIHRyYWluaW5nLCAzMCUgdGVzdGluZyoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCmluZD1zYW1wbGUoMiwgbnJvdyhkYXRhKSwgcmVwbGFjZT1UUlVFLCBwcm9iPWMoMC43MCAsIDAuMzApKQ0KdHJhaW5fZGF0YT1kYXRhW2luZD09MSxdDQp0ZXN0X2RhdGE9ZGF0YVtpbmQ9PTIsXQ0KZGltKHRyYWluX2RhdGEpDQpkaW0odGVzdF9kYXRhKQ0KYGBgDQoNCldlIHBhcnRpdGlvbiB0aGUgZGF0YSB0aGUgZGF0YSBpbnRvICg3MCUgdHJhaW5pbmcsIDMwJSB0ZXN0aW5nKS4gVGhpcyByZXN1bHQgaW4gNzA1IG9iamVjdCBpbiB0aGUgdHJhaW5pbmcgc2V0IGFuZCAyOTUgb2JqZWN0IGluIHRoZSB0ZXN0aW5nIHNldC4NCg0KIyMjIyAqKmluZm9ybWF0aW9uIGdhaW4qKg0KDQpgYGB7cn0NCg0KbGlicmFyeShwYXJ0eSkNCm15Rm9ybXVsYTwtIHdpbGxfZ29fdG9fY29sbGVnZX4gKyB0eXBlX3NjaG9vbCtzY2hvb2xfYWNjcmVkaXRhdGlvbitnZW5kZXIraW50ZXJlc3QrcmVzaWRlbmNlK3BhcmVudF9hZ2UrcGFyZW50X3NhbGFyeStob3VzZV9hcmVhK2F2ZXJhZ2VfZ3JhZGVzK3BhcmVudF93YXNfaW5fY29sbGVnZQ0KDQpkYXRhc2V0X2N0cmVlPC1jdHJlZShteUZvcm11bGEsIGRhdGE9dHJhaW5fZGF0YSkNCnRhYmxlKHByZWRpY3QoZGF0YXNldF9jdHJlZSksIHRyYWluX2RhdGEkd2lsbF9nb190b19jb2xsZWdlKQ0KcGxvdChkYXRhc2V0X2N0cmVlLHR5cGU9InNpbXBsZSIpDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KGRhdGFzZXRfY3RyZWUsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpDQpyZXN1bHQ8LXRhYmxlKHRlc3RQcmVkLCB0ZXN0X2RhdGEkd2lsbF9nb190b19jb2xsZWdlICkNCmxpYnJhcnkoY2FyZXQpDQpjb19yZXN1bHQgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdCxwb3NpdGl2ZT0iMSIpDQpwcmludChjb19yZXN1bHQpDQpgYGANCg0KIVtdKGltYWdlcy9XaGF0c0FwcCUyMEltYWdlJTIwMjAyMy0xMS0zMCUyMGF0JTIwMTkuMzYuMjlfMGU4NWU0MTguanBnKQ0KDQp0aGlzIG1hdHJpeCBiZWxvdyBzaG93IHRoZSBldmFsdWF0aW9uIG1vZGVsIG9uIHRlc3RpbmcgZGF0YQ0KDQp8IEV2YWx1YXRpb24gbWV0aG9kIHwgTWV0aG9kIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICAgICAgICB8IDgyLjM3JSB8DQp8IEVycm9yIHJhdGUgICAgICAgIHwgMTcuNjMlIHwNCnwgU2Vuc2l0aXZpdHkgICAgICAgfCA3NS44OSUgfA0KfCBTcGVjaWZpY2l0eSAgICAgICB8IDg4LjMxJSB8DQp8IFByZWNpc2lvbiAgICAgICAgIHwgODUuNjAlIHwNCg0KSW4gdGhpcyBkZWNpc2lvbiB0cmVlIG1vZGVsLCB0aGUgYXZlcmFnZSBncmFkZSB3aXRoIHRoZSBoaWdoZXN0IGluZm9ybWF0aW9uIGdhaW4gaXMgc2VsZWN0ZWQgYXMgdGhlIHJvb3QgYXR0cmlidXRlLiBUaGlzIG1lYW5zIHRoYXQgdGhlIGF2ZXJhZ2UgZ3JhZGUgaXMgY29uc2lkZXJlZCB0aGUgbW9zdCBpbmZvcm1hdGl2ZSBmZWF0dXJlIGZvciBjbGFzc2lmeWluZyB0aGUgZGF0YS4NCg0KVG8gZW5oYW5jZSB0aGUgbW9kZWwncyBwcmVkaWN0aXZlIGNhcGFiaWxpdHksIGFkZGl0aW9uYWwgc3BsaXR0aW5nIGNyaXRlcmlhIGFyZSBpbmNvcnBvcmF0ZWQgYmFzZWQgb24gdGhlaXIgaW5mb3JtYXRpb24gZ2FpbiB2YWx1ZXMuIFRoZXNlIGNyaXRlcmlhIGluY2x1ZGUgdHlwZV9zY2hvb2wsIGludGVyZXN0LCByZXNpZGVuY2UsIHBhcmVudF9zYWxhcnksIGFuZCBob3VzZV9hcmVhLiBCeSBjb25zaWRlcmluZyB0aGVzZSBhdHRyaWJ1dGVzLCB0aGUgbW9kZWwgYWltcyB0byBjYXB0dXJlIG1vcmUgcmVsZXZhbnQgaW5mb3JtYXRpb24gZnJvbSB0aGUgZGF0YSBhbmQgaW1wcm92ZSBpdHMgY2xhc3NpZmljYXRpb24gYWNjdXJhY3kuDQoNClRoZSBtb2RlbCB0aGVuIHV0aWxpemVzIHRoZSBob3VzZSBhcmVhIGFuZCBhdmVyYWdlIGdyYWRlcyBhcyBzcGxpdHRpbmcgY3JpdGVyaWEgdG8gc2VwYXJhdGUgdGhlIGRhdGEgaW50byBkaWZmZXJlbnQgYnJhbmNoZXMgb2YgdGhlIHRyZWUgaW4gbXVsdGlwbGVzIGxldmVscw0KDQpIb3dldmVyLCBpdCdzIHdvcnRoIG5vdGluZyB0aGF0IHRoZSBkZWNpc2lvbiB0cmVlIGluIHRoaXMgY2FzZSBpcyBjb25zaWRlcmVkIHRvIGhhdmUgYSBsb3cgbGV2ZWwuIFRoaXMgaW1wbGllcyB0aGF0IHRoZSB0cmVlIGlzIHJlbGF0aXZlbHkgc2hhbGxvdyBhbmQgbWF5IG5vdCBiZSBhYmxlIHRvIGNhcHR1cmUgaW50cmljYXRlIHBhdHRlcm5zIG9yIHJlbGF0aW9uc2hpcHMgcHJlc2VudCBpbiB0aGUgZGF0YS4gRm9yIGV4YW1wbGUgZ2VuZGVyLCBwYXJlbnRfd2FzX2luX2NvbGxlZ2UsIHBhcmVudCBhZ2UsIGFuZCBzY2hvb2wgYWNjcmVkaXRhdGlvbiBhcmUgbm90IGluY2x1ZGVkIGluIHRoZSB0cmVlIGR1ZSB0byBsaW1pdGVkIGF2YWlsYWJpbGl0eSBvZiB0aGUgZGF0YSBvciB0aGVpciBsb3dlciBpbmZvcm1hdGlvbiBnYWluIHZhbHVlcy4NCg0KIyMjIyAqKkdhaW4gcmF0aW8qKg0KDQpgYGB7cn0NCmxpYnJhcnkoUldla2EpDQpDNDVGaXQgPC0gSjQ4KCB3aWxsX2dvX3RvX2NvbGxlZ2V+LixkYXRhPXRyYWluX2RhdGEpDQp0YWJsZSh0cmFpbl9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSwgcHJlZGljdChDNDVGaXQpKQ0KcGxvdChDNDVGaXQsdHlwZSA9ICJzaW1wbGUiKQ0KdGVzdFByZWQgPC0gcHJlZGljdChDNDVGaXQsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpDQpyZXN1bHRzIDwtIGNvbmZ1c2lvbk1hdHJpeCh0ZXN0UHJlZCwgdGVzdF9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSwgcG9zaXRpdmUgPSAiMSIpDQpwcmludChyZXN1bHRzKQ0KYGBgDQoNCiFbXShpbWFnZXMvV2hhdHNBcHAlMjBJbWFnZSUyMDIwMjMtMTEtMzAlMjBhdCUyMDE5LjUzLjMwXzRlYWNiYzg2LmpwZykNCg0KdGhpcyBtYXRyaXggYmVsb3cgc2hvdyB0aGUgZXZhbHVhdGlvbiBtb2RlbCBvbiB0ZXN0aW5nIGRhdGENCg0KfCBFdmFsdWF0aW9uIG1ldGhvZCB8IE1ldGhvZCB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgICAgICAgfCA4MC4zNCUgfA0KfCBFcnJvciByYXRlICAgICAgICB8IDE5LjY2JSB8DQp8IFNlbnNpdGl2aXR5ICAgICAgIHwgNzcuMzAlIHwNCnwgU3BlY2lmaWNpdHkgICAgICAgfCA4My4xMiUgfA0KfCBQcmVjaXNpb24gICAgICAgICB8IDgwLjc0JSB8DQoNCkluIGNvbnRyYXN0IHRvIHRoZSBwcmV2aW91cyBkZWNpc2lvbiB0cmVlLCB0aGUgY3VycmVudCB0cmVlIGlzIG1vcmUgY29tcGxleCBhbmQgaW5jbHVkZXMgYWxsIHRoZSBhdHRyaWJ1dGVzIGV4Y2VwdCBmb3IgZ2VuZGVyLiBUaGlzIG1lYW5zIHRoYXQgdGhlIGRlY2lzaW9uIHRyZWUgdGFrZXMgaW50byBhY2NvdW50IGEgd2lkZXIgcmFuZ2Ugb2YgZmFjdG9ycyB0byBtYWtlIGl0cyBjbGFzc2lmaWNhdGlvbnMuIFRoZSBhdHRyaWJ1dGUgd2l0aCB0aGUgaGlnaGVzdCBHYWluIHJhdGlvLCB3aGljaCBtZWFzdXJlcyB0aGUgZWZmZWN0aXZlbmVzcyBvZiBhIHNwbGl0LCBpcyBjaG9zZW4gYXMgdGhlIHJvb3QgYXR0cmlidXRlLiBJbiB0aGlzIGNhc2UsIFBhcmVudCBzYWxhcnkgaXMgZGV0ZXJtaW5lZCB0byBoYXZlIHRoZSBoaWdoZXN0IEdhaW4gcmF0aW8gYW5kIGlzIHNlbGVjdGVkIGFzIHRoZSByb290IGF0dHJpYnV0ZS4NCg0KVGhlIG1vZGVsIHRoZW4gcmVwZWF0ZWRseSB1c2VzIGF2ZXJhZ2UgZ3JhZGVzIGFzIHRoZSBzcGxpdHRpbmcgY3JpdGVyaW9uIGF0IGRpZmZlcmVudCBsZXZlbHMgb2YgdGhlIHRyZWUuIFRoaXMgaW5kaWNhdGVzIHRoYXQgdGhlIGF2ZXJhZ2UgZ3JhZGVzIGFyZSBjb25zaWRlcmVkIGNydWNpYWwgZm9yIGNsYXNzaWZ5aW5nIHRoZSBkYXRhIGFuZCBmdXJ0aGVyIHN1YmRpdmlkaW5nIHRoZSBicmFuY2hlcy4NCg0KIyMjIyAqKkdpbmkgaW5kZXgqKg0KDQpgYGB7cn0NCmxpYnJhcnkocnBhcnQpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpjYXJ0X2ZpdD1ycGFydCh3aWxsX2dvX3RvX2NvbGxlZ2V+LiwgZGF0YT10cmFpbl9kYXRhLCBtZXRob2Q9ImNsYXNzIixjcD0wLjAwOCkNCg0KDQpycGFydC5wbG90KGNhcnRfZml0KQ0KDQp0ZXN0UHJlZDwtIHByZWRpY3QoY2FydF9maXQsbmV3ZGF0YT10ZXN0X2RhdGEsdHlwZT0iY2xhc3MiKQ0KcmVzdWx0czwtIGNvbmZ1c2lvbk1hdHJpeCh0ZXN0UHJlZCx0ZXN0X2RhdGEkd2lsbF9nb190b19jb2xsZWdlLHBvc2l0aXZlPSIxIikNCnByaW50KHJlc3VsdHMpDQoNCmBgYA0KDQp0aGlzIG1hdHJpeCBiZWxvdyBzaG93IHRoZSBldmFsdWF0aW9uIG1vZGVsIG9uIHRlc3RpbmcgZGF0YQ0KDQp8IEV2YWx1YXRpb24gbWV0aG9kIHwgTWV0aG9kIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICAgICAgICB8IDc5LjY2JSB8DQp8IEVycm9yIHJhdGUgICAgICAgIHwgMjAuMzQlIHwNCnwgU2Vuc2l0aXZpdHkgICAgICAgfCA3OC4wMSUgfA0KfCBTcGVjaWZpY2l0eSAgICAgICB8IDgxLjE3JSB8DQp8IFByZWNpc2lvbiAgICAgICAgIHwgNzkuMTQlIHwNCg0KSW4gdGhpcyBwYXJ0aWN1bGFyIHBhcnRpdGlvbiBvZiB0aGUgZGVjaXNpb24gdHJlZSwgUGFyZW50IHNhbGFyeSB3YXMgb25jZSBhZ2FpbiBjaG9zZW4gYXMgdGhlIHJvb3QgYXR0cmlidXRlIGR1ZSB0byBpdHMgaGlnaCBHYWluIGluZGV4Lg0KDQpHZW5kZXIgYW5kIHJlc2lkZW5jZSwgb24gdGhlIG90aGVyIGhhbmQsIHdlcmUgbm90IGluY2x1ZGVkIGluIHRoaXMgcGFydGl0aW9uIG9mIHRoZSB0cmVlLiBXaXRob3V0IHN1ZmZpY2llbnQgZGF0YSBvciBhIHNpZ25pZmljYW50IGluZm9ybWF0aW9uIGdhaW4gYXNzb2NpYXRlZCB3aXRoIHRoZXNlIGF0dHJpYnV0ZXMsIHRoZSBtb2RlbCBtYXkgbm90IGhhdmUgZW5vdWdoIGV2aWRlbmNlIHRvIHV0aWxpemUgdGhlbSBmb3IgYWNjdXJhdGUgY2xhc3NpZmljYXRpb24uDQoNCkl0J3Mgd29ydGggbm90aW5nIHRoYXQgZ2VuZGVyIHdhcyBub3QgaW5jbHVkZWQgaW4gdGhpcyBwYXJ0aXRpb24sIGFuZCBpdCBpcyBpbXBvcnRhbnQgdG8gaGlnaGxpZ2h0IHRoYXQgdGhlIGFjY3VyYWN5IG9mIHRoaXMgc3BlY2lmaWMgbW9kZWwgd2FzIHJlbGF0aXZlbHkgbG93ZXIsIGF0IDc5LjY2JS4NCg0KfCA4MCUgdHJhaW5pbmcsIDIwJSB0ZXN0aW5nIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KaW5kPXNhbXBsZSgyLCBucm93KGRhdGEpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjgwICwgMC4yMCkpDQp0cmFpbl9kYXRhPWRhdGFbaW5kPT0xLF0NCnRlc3RfZGF0YT1kYXRhW2luZD09MixdDQpkaW0odHJhaW5fZGF0YSkNCmRpbSh0ZXN0X2RhdGEpDQpgYGANCg0KV2UgcGFydGl0aW9uIHRoZSBkYXRhIHRoZSBkYXRhIGludG8gKDgwJSB0cmFpbmluZywgMjAlIHRlc3RpbmcpLiBUaGlzIHJlc3VsdCBpbiA4MDIgb2JqZWN0IGluIHRoZSB0cmFpbmluZyBzZXQgYW5kIDE5OCBvYmplY3QgaW4gdGhlIHRlc3Rpbmcgc2V0Lg0KDQojIyMjICoqaW5mb3JtYXRpb24gZ2FpbioqDQoNCmBgYHtyfQ0KbGlicmFyeShwYXJ0eSkNCm15Rm9ybXVsYTwtIHdpbGxfZ29fdG9fY29sbGVnZX4gKyB0eXBlX3NjaG9vbCtzY2hvb2xfYWNjcmVkaXRhdGlvbitnZW5kZXIraW50ZXJlc3QrcmVzaWRlbmNlK3BhcmVudF9hZ2UrcGFyZW50X3NhbGFyeStob3VzZV9hcmVhK2F2ZXJhZ2VfZ3JhZGVzK3BhcmVudF93YXNfaW5fY29sbGVnZQ0KDQpkYXRhc2V0X2N0cmVlPC1jdHJlZShteUZvcm11bGEsIGRhdGE9dHJhaW5fZGF0YSkNCnRhYmxlKHByZWRpY3QoZGF0YXNldF9jdHJlZSksIHRyYWluX2RhdGEkd2lsbF9nb190b19jb2xsZWdlKQ0KcGxvdChkYXRhc2V0X2N0cmVlLHR5cGU9InNpbXBsZSIpDQp0ZXN0UHJlZCA8LSBwcmVkaWN0KGRhdGFzZXRfY3RyZWUsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpDQpyZXN1bHQ8LXRhYmxlKHRlc3RQcmVkLCB0ZXN0X2RhdGEkd2lsbF9nb190b19jb2xsZWdlICkNCmxpYnJhcnkoY2FyZXQpDQpjb19yZXN1bHQgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdCxwb3NpdGl2ZT0iMSIpDQpwcmludChjb19yZXN1bHQpDQpgYGANCg0KIVtdKGltYWdlcy9XaGF0c0FwcCUyMEltYWdlJTIwMjAyMy0xMS0zMCUyMGF0JTIwMjAuMTUuMzZfZTk1YzI1ZTEuanBnKQ0KDQp0aGlzIG1hdHJpeCBiZWxvdyBzaG93IHRoZSBldmFsdWF0aW9uIG1vZGVsIG9uIHRlc3RpbmcgZGF0YQ0KDQp8IEV2YWx1YXRpb24gbWV0aG9kIHwgTWV0aG9kICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgODMuODQgJSB8DQp8IEVycm9yIHJhdGUgICAgICAgIHwgMTYuMTYlICB8DQp8IFNlbnNpdGl2aXR5ICAgICAgIHwgODYuNzMlICB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgODEuMDAlICB8DQp8IFByZWNpc2lvbiAgICAgICAgIHwgODEuNzMlICB8DQoNCkluIHRoaXMgcGFydGl0aW9uIG9mIHRoZSBkZWNpc2lvbiB0cmVlLCB0aGUgYXZlcmFnZSBncmFkZSB3aXRoIHRoZSBoaWdoZXN0IGluZm9ybWF0aW9uIGdhaW4gaXMgc2VsZWN0ZWQgYXMgdGhlIHJvb3QgYXR0cmlidXRlLiBUaGUgc2VsZWN0aW9uIGlzIGJhc2VkIG9uIHRoZSBmYWN0IHRoYXQgdGhlIGF2ZXJhZ2UgZ3JhZGUgYXR0cmlidXRlIGhhcyB0aGUgbG93ZXN0IGNvbmRpdGlvbmFsIGVudHJvcHksIHRoZSBtb2RlbCBhaW1zIHRvIGNyZWF0ZSBhIG1vcmUgZWZmZWN0aXZlIHNwbGl0IGFuZCBpbXByb3ZlIHRoZSBjbGFzc2lmaWNhdGlvbiBhY2N1cmFjeS4gYWxzbyB0byBtZW50aW9uIHRoYXQgYXR0cmlidXRlIHdhcyBub3QgaW5jbHVkZWQgaW4gdGhlIHRyZWUgZ2VuZGVyLHBhcmVudCB3YXMgaW4gY29sbGFnZSwgcGFyZW50IGFnZQ0KDQpPbiBhIHBvc2l0aXZlIG5vdGUsIHRoZSBhY2N1cmFjeSBpbiB0aGlzIHBhcnRpdGlvbiBoYXMgaW5jcmVhc2VkIGJ5IDEuNDclIGNvbXBhcmVkIHRvIHRoZSBwcmV2aW91cyBwYXJ0aXRpb24uIFRoaXMgaW1wcm92ZW1lbnQgaW4gYWNjdXJhY3kgY2FuIGJlIGF0dHJpYnV0ZWQgdG8gdGhlIGZhY3QgdGhhdCB0aGUgdHJhaW5pbmcgZGF0YSBoYXMgYmVlbiBpbmNyZWFzZWQgYnkgMTAlLiBCeSBoYXZpbmcgbW9yZSBkYXRhIGZvciB0cmFpbmluZywgdGhlIG1vZGVsIGhhcyBhIGxhcmdlciBhbmQgbW9yZSBkaXZlcnNlIHNldCBvZiBleGFtcGxlcyB0byBsZWFybiBmcm9tLCBhbGxvd2luZyBpdCB0byBtYWtlIG1vcmUgYWNjdXJhdGUgcHJlZGljdGlvbnMuDQoNCiMjIyMgKipHYWluIHJhdGlvKioNCg0KYGBge3J9DQpsaWJyYXJ5KFJXZWthKQ0KDQoNCkM0NUZpdCA8LSBKNDgoIHdpbGxfZ29fdG9fY29sbGVnZX4uLGRhdGE9dHJhaW5fZGF0YSkNCnRhYmxlKHRyYWluX2RhdGEkd2lsbF9nb190b19jb2xsZWdlLCBwcmVkaWN0KEM0NUZpdCkpDQoNCnBsb3QoQzQ1Rml0LHR5cGUgPSAic2ltcGxlIikNCnRlc3RQcmVkIDwtIHByZWRpY3QoQzQ1Rml0LCBuZXdkYXRhID0gdGVzdF9kYXRhKQ0KcmVzdWx0cyA8LSBjb25mdXNpb25NYXRyaXgodGVzdFByZWQsIHRlc3RfZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UsIHBvc2l0aXZlID0gIjEiKQ0KcHJpbnQocmVzdWx0cykNCmBgYA0KDQohW10oaW1hZ2VzL1doYXRzQXBwJTIwSW1hZ2UlMjAyMDIzLTExLTMwJTIwYXQlMjAyMC4xNy4yNV9jMDJhMWI4MS5qcGcpDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgODAuMDMlIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAxOS45NyUgfA0KfCBTZW5zaXRpdml0eSAgICAgICB8IDg0LjY5JSB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgNzYuMDAlIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA3Ny41NyUgfA0KDQpJbiB0aGlzIHBhcnRpY3VsYXIgbW9kZWwgdGhhdCB1dGlsaXplcyB0aGUgZ2FpbiByYXRpbywgaXQgaXMgb2JzZXJ2ZWQgdGhhdCB0aGUgZGVjaXNpb24gdHJlZSBpcyBvbmNlIGFnYWluIG1vcmUgY29tcGxleCBjb21wYXJlZCB0byB0aGUgb3RoZXIgbW9kZWxzLiBUaGUgYXR0cmlidXRlIGNob3NlbiBhcyB0aGUgcm9vdCBhdHRyaWJ1dGUgaXMgcGFyZW50IHNhbGFyeSwgYmFzZWQgb24gaXRzIGhpZ2ggcHVyaXR5IGFuZCByZXN1bHRpbmcgaGlnaCBnYWluIHJhdGlvLiBIb3dldmVyLCBpdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZSBnZW5kZXIgYXR0cmlidXRlIHdhcyBub3QgaW5jbHVkZWQgaW4gdGhlIGRlY2lzaW9uIHRyZWUuIFVsdGltYXRlbHksIHRoZSBnYWluIHJhdGlvIG1vZGVsJ3MgY29uc3RydWN0aW9uIGludm9sdmVkIGNhcmVmdWwgY29uc2lkZXJhdGlvbnMgYW5kIHByaW9yaXRpemVkIHRoZSBwYXJlbnQncyBzYWxhcnkgYXMgcm9vdA0KDQojIyMjICoqR2luaSBpbmRleCoqDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmNhcnRfZml0PXJwYXJ0KHdpbGxfZ29fdG9fY29sbGVnZX4uLCBkYXRhPXRyYWluX2RhdGEsIG1ldGhvZD0iY2xhc3MiLGNwPTAuMDA4KQ0KDQoNCnJwYXJ0LnBsb3QoY2FydF9maXQpDQoNCnRlc3RQcmVkPC0gcHJlZGljdChjYXJ0X2ZpdCxuZXdkYXRhPXRlc3RfZGF0YSx0eXBlPSJjbGFzcyIpDQpyZXN1bHRzPC0gY29uZnVzaW9uTWF0cml4KHRlc3RQcmVkLHRlc3RfZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UscG9zaXRpdmU9IjEiKQ0KcHJpbnQocmVzdWx0cykNCg0KYGBgDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgODAuODElIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAxOS4xOSUgfA0KfCBTZW5zaXRpdml0eSAgICAgICB8IDc2LjUzJSB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgODUuMDAlIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA4My4zMyUgfA0KDQpmb3IgdGhlIGZpcnN0IHRpbWUgaG91c2UgYXJlYSB3YXMgY2hvc2VuIGFzIHRoZSByb290IGR1ZSB0byBpdHMgaGlnaCBwdXJpdHksIGFzIGluZGljYXRlZCBieSB0aGUgaGlnaCBHaW5pIGluZGV4Lg0KDQpUaGUgbW9kZWwgcmVsaWVzIG9uIHRoZSBwYXJlbnRfc2FsYXJ5IGF0dHJpYnV0ZSB0byBzcGxpdCB0aGUgdHJlZSBtdWx0aXBsZSB0aW1lcywgaW5kaWNhdGluZyBpdHMgaW1wb3J0YW5jZSBpbiBkZXRlcm1pbmluZyB0aGUgY2xhc3NpZmljYXRpb24uIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgcGFyZW50J3Mgc2FsYXJ5IGhhcyBhIHNpZ25pZmljYW50IGltcGFjdCBvbiB3aGV0aGVyIGEgc3R1ZGVudCB3aWxsIGdvIHRvIGNvbGxlZ2Ugb3Igbm90LCBhbmQgaXQgaXMgcmVwZWF0ZWRseSB1c2VkIGFzIGEgY3JpdGVyaW9uIGZvciBmdXJ0aGVyIGJyYW5jaGluZyBpbiB0aGUgdHJlZS4NCg0KSXQncyB3b3J0aCBub3RpbmcgdGhhdCAyMCUgb2YgdGhlIGRhdGFzZXQgZm9sbG93cyB0aGUgcnVsZToNCg0KSUYgaG91c2VfYXJlYSBcPCAwLjU0IGFuZCBwYXJlbnRfc2FsYXJ5IFw8IDAuNDIsIFRIRU4gd2lsbCBnbyB0byBjb2xsZWdlID0gMS4NCg0KVGhpcyBydWxlIGltcGxpZXMgdGhhdCB3aGVuIHRoZSBob3VzZSBhcmVhIGlzIGJlbG93IGEgY2VydGFpbiB0aHJlc2hvbGQgYW5kIHRoZSBwYXJlbnQncyBzYWxhcnkgaXMgYmVsb3cgYW5vdGhlciB0aHJlc2hvbGQsIHRoZXJlIGlzIGEgaGlnaGVyIGxpa2VsaWhvb2Qgb2YgdGhlIHN0dWRlbnQgZ29pbmcgdG8gY29sbGVnZS4NCg0KTW9yZW92ZXIsIDI4JSBvZiB0aGUgZGF0YXNldCBmb2xsb3dzIHRoZSBydWxlOg0KDQpJRiBob3VzZV9hcmVhIFw+PSAwLjU0IGFuZCBwYXJlbnRfc2FsYXJ5IFw+PSAwLjM3IGFuZCB0eXBlX3NjaG9vbCA9IDEsIFRIRU4gd2lsbCBnbyB0byBjb2xsZWdlID0gMS4gVGhpcyBydWxlIHN1Z2dlc3RzIHRoYXQgd2hlbiB0aGUgaG91c2UgYXJlYSBpcyBhYm92ZSBhIGNlcnRhaW4gdGhyZXNob2xkLCB0aGUgcGFyZW50J3Mgc2FsYXJ5IGlzIGFib3ZlIGFub3RoZXIgdGhyZXNob2xkLCBhbmQgdGhlIHR5cGUgb2Ygc2Nob29sIGlzIDEsIHRoZXJlIGlzIGEgaGlnaGVyIHByb2JhYmlsaXR5IG9mIHRoZSBzdHVkZW50IGdvaW5nIHRvIGNvbGxlZ2UuDQoNCk92ZXJhbGwsIHRoZXNlIHJ1bGVzIGNvdmVyIGEgc2lnbmlmaWNhbnQgcG9ydGlvbiBvZiB0aGUgZGF0YXNldCwgNDglIG9mIGl0LiBUaGlzIGluZGljYXRlcyB0aGF0IGEgbGFyZ2UgYW1vdW50IG9mIGRhdGEgY2FuIGJlIGNsYXNzaWZpZWQgYmFzZWQgb24gdGhlc2Ugc3BlY2lmaWMgcnVsZXMsIHByb3ZpZGluZyB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSBmYWN0b3JzIHRoYXQgaW5mbHVlbmNlIGEgc3R1ZGVudCdzIGxpa2VsaWhvb2Qgb2YgYXR0ZW5kaW5nIGNvbGxlZ2UuIHdlIG9ubHkgbWVudGlvbmVkIHRob3NlIHR3byBydWxlcyBkdW8gdG8gdGhlaXIgaGlnaCBwZXJjZW50YWdlIG9mIGRhdGENCg0KfCA1MCUgdHJhaW5pbmcsIDUwJSB0ZXN0aW5nIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KaW5kPXNhbXBsZSgyLCBucm93KGRhdGEpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjUwICwgMC41MCkpDQp0cmFpbl9kYXRhPWRhdGFbaW5kPT0xLF0NCnRlc3RfZGF0YT1kYXRhW2luZD09MixdDQpkaW0odHJhaW5fZGF0YSkNCmRpbSh0ZXN0X2RhdGEpDQoNCmBgYA0KDQpXZSBwYXJ0aXRpb24gdGhlIGRhdGEgdGhlIGRhdGEgaW50byAoNTAlIHRyYWluaW5nLCA1MCUgdGVzdGluZykuIFRoaXMgcmVzdWx0IGluIDQ5MyBvYmplY3QgaW4gdGhlIHRyYWluaW5nIHNldCBhbmQgNTA3IG9iamVjdCBpbiB0aGUgdGVzdGluZyBzZXQuDQoNCiMjIyMgKippbmZvcm1hdGlvbiBnYWluKioNCg0KYGBge3J9DQpsaWJyYXJ5KHBhcnR5KQ0KbXlGb3JtdWxhPC0gd2lsbF9nb190b19jb2xsZWdlfiArIHR5cGVfc2Nob29sK3NjaG9vbF9hY2NyZWRpdGF0aW9uK2dlbmRlcitpbnRlcmVzdCtyZXNpZGVuY2UrcGFyZW50X2FnZStwYXJlbnRfc2FsYXJ5K2hvdXNlX2FyZWErYXZlcmFnZV9ncmFkZXMrcGFyZW50X3dhc19pbl9jb2xsZWdlDQoNCmRhdGFzZXRfY3RyZWU8LWN0cmVlKG15Rm9ybXVsYSwgZGF0YT10cmFpbl9kYXRhKQ0KdGFibGUocHJlZGljdChkYXRhc2V0X2N0cmVlKSwgdHJhaW5fZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpIA0KDQpwbG90KGRhdGFzZXRfY3RyZWUsdHlwZT0ic2ltcGxlIikNCnRlc3RQcmVkIDwtIHByZWRpY3QoZGF0YXNldF9jdHJlZSwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCnJlc3VsdDwtdGFibGUodGVzdFByZWQsIHRlc3RfZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UgKQ0KDQpsaWJyYXJ5KGNhcmV0KQ0KY29fcmVzdWx0IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHQsIHBvc2l0aXZlID0gIjEiKQ0KcHJpbnQoY29fcmVzdWx0KQ0KDQpgYGANCg0KIVtdKGltYWdlcy9XaGF0c0FwcCUyMEltYWdlJTIwMjAyMy0xMS0zMCUyMGF0JTIwMjAuMjAuMDNfYmEwYjNmY2IuanBnKQ0KDQp0aGlzIG1hdHJpeCBiZWxvdyBzaG93IHRoZSBldmFsdWF0aW9uIG1vZGVsIG9uIHRlc3RpbmcgZGF0YQ0KDQp8IEV2YWx1YXRpb24gbWV0aG9kIHwgTWV0aG9kIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICAgICAgICB8IDg0LjIyJSB8DQp8IEVycm9yIHJhdGUgICAgICAgIHwgMTUuNzglIHwNCnwgU2Vuc2l0aXZpdHkgICAgICAgfCA4OS41MyUgfA0KfCBTcGVjaWZpY2l0eSAgICAgICB8IDc4LjcxJSB8DQp8IFByZWNpc2lvbiAgICAgICAgIHwgODEuMzQlIHwNCg0KSW4gdGhpcyBkZWNpc2lvbiB0cmVlIG1vZGVsLCB0aGUgcm9vdCBhdHRyaWJ1dGUgaXMgZGV0ZXJtaW5lZCBieSBzZWxlY3RpbmcgdGhlIGF2ZXJhZ2UgZ3JhZGUgd2l0aCB0aGUgaGlnaGVzdCBpbmZvcm1hdGlvbiBnYWluLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBhdmVyYWdlIGdyYWRlIGlzIGNvbnNpZGVyZWQgdGhlIG1vc3QgaW5mb3JtYXRpdmUgZmVhdHVyZSBmb3IgY2xhc3NpZnlpbmcgdGhlIGRhdGEsIGFzIGl0IHByb3ZpZGVzIHNpZ25pZmljYW50IGluc2lnaHRzIGludG8gdGhlIGNsYXNzaWZpY2F0aW9uIHByb2Nlc3MuDQoNClRvIGltcHJvdmUgdGhlIG1vZGVsJ3MgcHJlZGljdGl2ZSBjYXBhYmlsaXR5LCBhZGRpdGlvbmFsIHNwbGl0dGluZyBjcml0ZXJpYSBhcmUgaW5jb3Jwb3JhdGVkIGJhc2VkIG9uIHRoZWlyIGluZm9ybWF0aW9uIGdhaW4gdmFsdWVzLiBUaGVzZSBjcml0ZXJpYSBpbmNsdWRlIHR5cGVfc2Nob29sLCBpbnRlcmVzdCwgcmVzaWRlbmNlLCBwYXJlbnRfc2FsYXJ5LCBob3VzZV9hcmVhLCBhbmQgd2hldGhlciB0aGUgcGFyZW50IHdhcyBpbiBjb2xsZWdlLiBCeSBjb25zaWRlcmluZyB0aGVzZSBhdHRyaWJ1dGVzLCB0aGUgbW9kZWwgYWltcyB0byBjYXB0dXJlIG1vcmUgcmVsZXZhbnQgaW5mb3JtYXRpb24gYW5kIGVuaGFuY2UgaXRzIGFiaWxpdHkgdG8gY2xhc3NpZnkgdGhlIGRhdGEgYWNjdXJhdGVseS4NCg0KSW4gdGhlIHRyZWUgc3RydWN0dXJlLCB0aGUgbW9kZWwgdXRpbGl6ZXMgYm90aCB0aGUgaG91c2UgYXJlYSBhbmQgYXZlcmFnZSBncmFkZXMgYXMgc3BsaXR0aW5nIGNyaXRlcmlhIGluIG11bHRpcGxlIGxldmVscy4gVGhpcyBtZWFucyB0aGF0IHRoZSBkYXRhIGlzIGRpdmlkZWQgaW50byBkaWZmZXJlbnQgYnJhbmNoZXMgb2YgdGhlIHRyZWUgYmFzZWQgb24gdGhlc2UgYXR0cmlidXRlcywgYWxsb3dpbmcgZm9yIG1vcmUgcmVmaW5lZCBjbGFzc2lmaWNhdGlvbi4NCg0KZ2VuZGVyLCBwYXJlbnQgYWdlLCBhbmQgc2Nob29sIGFjY3JlZGl0YXRpb24gYXJlIG5vdCBpbmNsdWRlZCBpbiB0aGUgdHJlZSBkdWUgdG8gZWl0aGVyIGxpbWl0ZWQgYXZhaWxhYmlsaXR5IG9mIHRoZSBkYXRhIG9yIHRoZWlyIGxvd2VyIGluZm9ybWF0aW9uIGdhaW4gdmFsdWVzLCBpbmRpY2F0aW5nIHRoYXQgdGhleSBtYXkgaGF2ZSBsZXNzIGltcGFjdCBvbiB0aGUgY2xhc3NpZmljYXRpb24gcHJvY2VzcyBhY2NvcmRpbmcgdG8gdGhlIG1vZGVsJ3MgZXZhbHVhdGlvbi4NCg0KIyMjIyAqKkdhaW4gcmF0aW8qKg0KDQpgYGB7cn0NCmxpYnJhcnkoUldla2EpDQpDNDVGaXQgPC0gSjQ4KCB3aWxsX2dvX3RvX2NvbGxlZ2V+LixkYXRhPXRyYWluX2RhdGEpDQp0YWJsZSh0cmFpbl9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSwgcHJlZGljdChDNDVGaXQpKQ0KcGxvdChDNDVGaXQsdHlwZSA9ICJzaW1wbGUiKQ0KdGVzdFByZWQgPC0gcHJlZGljdChDNDVGaXQsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpDQpyZXN1bHRzIDwtIGNvbmZ1c2lvbk1hdHJpeCh0ZXN0UHJlZCwgdGVzdF9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSwgcG9zaXRpdmUgPSAiMSIpDQpwcmludChyZXN1bHRzKQ0KDQpgYGANCg0KIVtdKGltYWdlcy9XaGF0c0FwcCUyMEltYWdlJTIwMjAyMy0xMS0zMCUyMGF0JTIwMjAuMjAuMzNfYTM4MWIxMGEuanBnKQ0KDQp0aGlzIG1hdHJpeCBiZWxvdyBzaG93IHRoZSBldmFsdWF0aW9uIG1vZGVsIG9uIHRlc3RpbmcgZGF0YQ0KDQp8IEV2YWx1YXRpb24gbWV0aG9kIHwgTWV0aG9kIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tfA0KfCBBY2N1cmFjeSAgICAgICAgICB8IDg1LjIxJSB8DQp8IEVycm9yIHJhdGUgICAgICAgIHwgMTQuNzklIHwNCnwgU2Vuc2l0aXZpdHkgICAgICAgfCA4Ny42MCUgfA0KfCBTcGVjaWZpY2l0eSAgICAgICB8IDgyLjczJSB8DQp8IFByZWNpc2lvbiAgICAgICAgIHwgODQuMDElIHwNCg0KSW4gY29udHJhc3QgdG8gdGhlIHByZXZpb3VzIGRlY2lzaW9uIHRyZWUsIHRoZSBjdXJyZW50IHRyZWUgaXMgbW9yZSBjb21wbGV4IGFuZCBpbmNsdWRlcyBhbGwgdGhlIGF0dHJpYnV0ZXMgZXhjZXB0IGZvciBwYXJlbnQgYWdlLiBOb3RhYmx5LCBmb3IgdGhlIGZpcnN0IHRpbWUsIGdlbmRlciBpcyBpbmNsdWRlZCBhcyBhbiBhdHRyaWJ1dGUgaW4gdGhlIHRyZWUuIFRoaXMgaW5kaWNhdGVzIHRoYXQgdGhlIGRlY2lzaW9uIHRyZWUgbm93IHRha2VzIGludG8gYWNjb3VudCBhIHdpZGVyIHJhbmdlIG9mIGZhY3RvcnMsIGluY2x1ZGluZyBnZW5kZXIsIHRvIG1ha2UgaXRzIGNsYXNzaWZpY2F0aW9ucy4gQnkgaW5jb3Jwb3JhdGluZyB0aGVzZSBhZGRpdGlvbmFsIGF0dHJpYnV0ZXMsIHRoZSBtb2RlbCBhaW1zIHRvIGNhcHR1cmUgbW9yZSBudWFuY2VkIHBhdHRlcm5zIGFuZCByZWxhdGlvbnNoaXBzIGluIHRoZSBkYXRhLg0KDQpUaGUgYXR0cmlidXRlIHdpdGggdGhlIGhpZ2hlc3QgR2FpbiByYXRpbyBpcyBjaG9zZW4gYXMgdGhlIHJvb3QgYXR0cmlidXRlLiBJbiB0aGlzIHBhcnRpY3VsYXIgY2FzZSwgUGFyZW50IHNhbGFyeSBpcyBkZXRlcm1pbmVkIHRvIGhhdmUgdGhlIGhpZ2hlc3QgR2FpbiByYXRpbyBhbmQgaXMgc2VsZWN0ZWQgYXMgdGhlIHJvb3QgYXR0cmlidXRlLiBUaGlzIHN1Z2dlc3RzIHRoYXQgUGFyZW50IHNhbGFyeSBwcm92aWRlcyB0aGUgbW9zdCBzaWduaWZpY2FudCByZWR1Y3Rpb24gaW4gdW5jZXJ0YWludHkgYW5kIGlzIGNvbnNpZGVyZWQgY3J1Y2lhbCBmb3IgY2xhc3NpZnlpbmcgdGhlIGRhdGEgYWNjdXJhdGVseS4NCg0KIyMjIyAqKkdpbmkgaW5kZXgqKg0KDQpgYGB7cn0NCmxpYnJhcnkocnBhcnQpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpjYXJ0X2ZpdD1ycGFydCh3aWxsX2dvX3RvX2NvbGxlZ2V+LiwgZGF0YT10cmFpbl9kYXRhLCBtZXRob2Q9ImNsYXNzIixjcD0wLjAwOCkNCg0KcnBhcnQucGxvdChjYXJ0X2ZpdCkNCg0KdGVzdFByZWQ8LSBwcmVkaWN0KGNhcnRfZml0LG5ld2RhdGE9dGVzdF9kYXRhLHR5cGU9ImNsYXNzIikNCnJlc3VsdHM8LSBjb25mdXNpb25NYXRyaXgodGVzdFByZWQsdGVzdF9kYXRhJHdpbGxfZ29fdG9fY29sbGVnZSxwb3NpdGl2ZT0iMSIpDQpwcmludChyZXN1bHRzKQ0KYGBgDQoNCnRoaXMgbWF0cml4IGJlbG93IHNob3cgdGhlIGV2YWx1YXRpb24gbW9kZWwgb24gdGVzdGluZyBkYXRhDQoNCnwgRXZhbHVhdGlvbiBtZXRob2QgfCBNZXRob2QgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgICAgICAgIHwgODUuMjElIHwNCnwgRXJyb3IgcmF0ZSAgICAgICAgfCAyNC43OSUgfA0KfCBTZW5zaXRpdml0eSAgICAgICB8IDg4LjM3JSB8DQp8IFNwZWNpZmljaXR5ICAgICAgIHwgODEuOTMlIHwNCnwgUHJlY2lzaW9uICAgICAgICAgfCA4My41MiUgfA0KDQo6IFRoZSBhdHRyaWJ1dGUgY2hvc2VuIGFzIHRoZSByb290IGF0dHJpYnV0ZSBpbiB0aGlzIGRlY2lzaW9uIHRyZWUgaXMgYXZlcmFnZSBncmFkZXMsIHByaW1hcmlseSBkdWUgdG8gaXRzIGhpZ2ggcHVyaXR5LCBpbmRpY2F0aW5nIGEgaGlnaCBHaW5pIGluZGV4LiBUaGUgR2luaSBpbmRleCBtZWFzdXJlcyB0aGUgaW1wdXJpdHkgb2YgYSBub2RlIGluIGEgZGVjaXNpb24gdHJlZSwgYW5kIGEgaGlnaCBwdXJpdHkgdmFsdWUgc3VnZ2VzdHMgdGhhdCB0aGUgYXR0cmlidXRlIGlzIGVmZmVjdGl2ZSBmb3IgY2xhc3NpZmljYXRpb24uDQoNCjI3JSBvZiB0aGUgZGF0YXNldCBmb2xsb3dzIHRoZSBydWxlOg0KDQpJRiBhdmVyYWdlX2dyYWRlcz0rQSwrQixBIGFuZCBwYXJlbnRfc2FsYXJ5XD49MC4zNyBhbmQgaG91c2VfYXJlYVw+PTAuNDEgYW5kIHR5cGVfc2Nob29sPTEsIFRIRU4gd2lsbF9nb190b19jb2xsYWdlPTAuDQoNCkZ1cnRoZXJtb3JlLCAxNyUgb2YgdGhlIGRhdGFzZXQgZm9sbG93cyB0aGUgcnVsZToNCg0KSUYgYXZlcmFnZV9ncmFkZXM9QiwrQyBhbmQgaG91c2VfYXJlYVw8PTAuNDEsIFRIRU4gd2lsbF9nb190b19jb2xsYWdlPTEuDQoNClRoZXNlIHJ1bGVzIGhpZ2hsaWdodCB0aGUgY29tcGxleGl0eSBhbmQgdmFyaWFiaWxpdHkgaW4gZGV0ZXJtaW5pbmcgd2hldGhlciBhIHN0dWRlbnQgd2lsbCBnbyB0byBjb2xsZWdlLiBFdmVuIGlmIGEgc3R1ZGVudCBoYXMgc3RhYmxlIGZpbmFuY2lhbCBjaXJjdW1zdGFuY2VzIGFuZCBnb29kIGdyYWRlcywgaXQgZG9lcyBub3QgZ3VhcmFudGVlIHRoZWlyIGVucm9sbG1lbnQgaW4gY29sbGVnZS4gaW4gY29udHJhY3QgdG8gdGhlIHNlY29uZCB3aGljaCBtZWFuIE90aGVyIGZhY3RvcnMgY291bGQgcGxheSBhIHJvbGUgaW4gdGhlIGRlY2lzaW9uLW1ha2luZyBwcm9jZXNzLg0KDQp3ZSBvbmx5IG1lbnRpb25lZCB0aG9zZSB0d28gcnVsZXMgZHVvIHRvIHRoZWlyIGhpZ2ggcGVyY2VudGFnZSBvZiBkYXRhDQoNCiMjIGZpbmFsIGFuYWx5c2lzDQoNCiMjIyMgaW5mb3JtYXRpb24gZ2Fpbg0KDQpJbmZvcm1hdGlvbiBHYWluIGlzIGEgbWVhc3VyZSB1c2VkIGluIHRoZSBJRDMgYWxnb3JpdGhtIGZvciBhdHRyaWJ1dGUgc2VsZWN0aW9uIGluIGRlY2lzaW9uIHRyZWUgbGVhcm5pbmcuIEl0IHF1YW50aWZpZXMgdGhlIGFtb3VudCBvZiBpbmZvcm1hdGlvbiBvYnRhaW5lZCBhYm91dCB0aGUgY2xhc3MgdmFyaWFibGUgd2hlbiBhIGdpdmVuIGF0dHJpYnV0ZSBpcyB1c2VkIHRvIHNwbGl0IHRoZSBkYXRhLiBUaGUgSW5mb3JtYXRpb24gR2FpbiBpcyBjYWxjdWxhdGVkIGJ5IHN1YnN0cmFjdCB0aGUgZW50cm9weSBFeHBlY3RlZCBpbmZvcm1hdGlvbiBuZWVkZWQgdG8gY2xhc3NpZnkgYSB0dXBsZSBpbiBEIGEgd2l0aCBDb25kaXRpb25hbCBFbnRyb3B5IEluZm9ybWF0aW9uIG5lZWRlZCBhZnRlciB1c2luZyBBIHRvIHNwbGl0IEQgaW50byB2IHBhcnRpdGlvbnMsIHRvIGNsYXNzaWZ5IEQgLiBBIGhpZ2hlciBJbmZvcm1hdGlvbiBHYWluIGluZGljYXRlcyB0aGF0IHRoZSBhdHRyaWJ1dGUgaXMgbW9yZSBpbmZvcm1hdGl2ZSBhbmQgc2hvdWxkIGJlIGNob3NlbiBhcyB0aGUgc3BsaXR0aW5nIGNyaXRlcmlvbi4NCg0KZm9yIG91ciB0cmVlcywNCg0KYXZlcmFnZSBncmFkZSB3YXMgY2hvc2VuIGFzIHRoZSByb290IGF0dHJpYnV0ZSBmb3IgYWxsIDMgdHJlZXMgd2l0aCB0aGUgaGlnaGVzdCBpbmZvcm1hdGlvbiBnYWluIFRoaXMgYXR0cmlidXRlIFJlZmxlY3RzIHRoZSBsZWFzdCByYW5kb21uZXNzIG9yICJpbXB1cml0eSIgaW4gdGhlc2UgcGFydGl0aW9ucyBhbmQgZ3VhcmFudGVlcyB0aGF0IGEgc2ltcGxlIHRyZWUgaXMgZm91bmQuDQoNCnwgICAgICAgICAgICAgfCA1MCUtNTAlIHwgNzAlLTMwJSB8IDgwJS0yMCUgfA0KfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tLS18DQp8IEFjY3VyYWN5ICAgIHwgODQuMjIlICB8IDgyLjM3ICUgfCA4My44NCAlIHwNCnwgRXJyb3IgcmF0ZSAgfCAxNS43OCUgIHwgMTcuNjMlICB8IDE2LjE2JSAgfA0KfCBTZW5zaXRpdml0eSB8IDg5LjUzJSAgfCA3NS44OSUgIHwgODYuNzMlICB8DQp8IFNwZWNpZmljaXR5IHwgNzguNzElICB8IDg4LjMxJSAgfCA4MS4wMCUgIHwNCnwgUHJlY2lzaW9uICAgfCA4MS4zNCUgIHwgODUuNjAlICB8IDgxLjczJSAgfA0KDQp0aGUgZXZhbHVhdGlvbiBtZXRob2QgdGFibGUgc2hvd3MgdGhhdCB0aGUgYmVzdCBwYXJ0aXRvbiBmb3IgaW5mb3JtYXRpb24gZ2FpbiBzaG93cyB0aGF0IDUwJS01MCUgaXMgdGhlIGJlc3QgbW9kZWwgYmFzZWQgb24gYWNjdXJhY3kodHVwbGVzIHRoYXQgYXJlIGNvcnJlY3RseSBjbGFzc2lmaWVkKSB3aXRoIHByZWNudGFnZXMgODQuMjIlIGFuZCBTZW5zaXRpdml0eSAoVHJ1ZSBwb3NpdGl2ZSByZWNvZ25pdGlvbiByYXRlKSB3aXRoIDg5LjUzJSBidXQgdGhlIGxvd2VzdCBTcGVjaWZpY2l0eShUcnVlIG5lZ2F0aXZlIHJlY29nbml0aW9uIHJhdGUpIHdpdGggNzguNzElLiBvdmVyYWxsIGl0J3MgdGhlIGJlc3QgbW9kZWwgZm9yIGluZm9ybWF0aW9uIGdhaW4gaGFzIHRoZSBsb3dlc3QgZXJyb3IgcmF0ZSAxNS43OCUNCg0KIyMjIyBnYWluIHJhdGlvDQoNCkdhaW4gUmF0aW8gaXMgYW4gbm9ybWFsaXp0aW9uIGZvciBJbmZvcm1hdGlvbiBHYWluIGFuZCBpcyB1c2VkIGluIHRoZSBDNC41IGFsZ29yaXRobSwgd2hpY2ggaXMgYW4gZXh0ZW5zaW9uIG9mIElEMy4gV2hpbGUgSW5mb3JtYXRpb24gR2FpbiB0ZW5kcyB0byBmYXZvciBhdHRyaWJ1dGVzIHdpdGggbWFueSBtdWx0aXZhcmlhdGUgSXQgaXMgY2FsY3VsYXRlZCBieSBkaXZpZGluZyB0aGUgSW5mb3JtYXRpb24gR2FpbiBieSBTcGxpdEluZm8gYShEKS4NCg0KZm9yIG91ciB0cmVlcywNCg0KUGFyZW50IHNhbGFyeSB3YXMgY2hvc2VuIGFzIHRoZSByb290IGF0dHJpYnV0ZSBmb3IgYWxsIDMgdHJlZXMgZHVlIHRvIGl0cyBoaWdoIEdhaW4gcmF0aW8sIHRoZSB0cmVlcyBzcGxpdCBhcmUgdW5iYWxhbmNlZCBzaW5jZSBnYWluIHJhdGlvIFRlbmRzIHRvIHByZWZlciB1bmJhbGFuY2VkIHNwbGl0cyBpbiB3aGljaCBvbmUgcGFydGl0aW9uIGlzIG11Y2ggc21hbGxlciB0aGFuIHRoZSBvdGhlcnMNCg0KfCAgICAgICAgICAgICB8IDUwJS01MCUgfCA3MCUtMzAlIHwgODAlLTIwJSB8DQp8LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tfC0tLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgfCA4NS4yMSUgIHwgODAuMzQlICB8IDgwLjAzJSAgfA0KfCBFcnJvciByYXRlICB8IDE0Ljc5JSAgfCAxOS42NiUgIHwgMTkuOTclICB8DQp8IFNlbnNpdGl2aXR5IHwgODcuNjAlICB8IDc3LjMwJSAgfCA4NC42OSUgIHwNCnwgU3BlY2lmaWNpdHkgfCA4Mi43MyUgIHwgODMuMTIlICB8IDc2LjAwJSAgfA0KfCBQcmVjaXNpb24gICB8IDg0LjAxJSAgfCA4MC43NCUgIHwgNzcuNTclICB8DQoNCnRoZSBldmFsdWF0aW9uIG1ldGhvZCB0YWJsZSBzaG93cyB0aGF0IHRoZSBiZXN0IHBhcnRpdGlvbiBmb3IgZ2FpbiByYXRpbyBzaG93cyB0aGF0IDUwJS01MCUgaXMgdGhlIGJlc3QgbW9kZWwgYmFzZWQgb24gYWNjdXJhY3kodHVwbGVzIHRoYXQgYXJlIGNvcnJlY3RseSBjbGFzc2lmaWVkKSB3aXRoIHBlcmNudGFnZXMgODUuMjElIGFuZCBTZW5zaXRpdml0eSAoVHJ1ZSBwb3NpdGl2ZSByZWNvZ25pdGlvbiByYXRlKSB3aXRoIDg3LjYwJSBhbmQgUHJlY2lzaW9uKHR1cGxlcyBsYWJlbGVkIGFzIHBvc2l0aXZlIGFyZSBhY3R1YWxseSBwb3NpdGl2ZSkgd2l0aCBwZXJjZW50YWdlcyA4NC4wMSUgYnV0IHRoZSBsb3dlc3QgU3BlY2lmaWNpdHkoVHJ1ZSBuZWdhdGl2ZSByZWNvZ25pdGlvbiByYXRlKSB3aXRoIDgyLjczJS4gb3ZlcmFsbCBpdCdzIHRoZSBiZXN0IG1vZGVsIGRvciBnYWluIHJhdGlvIHdpdGggdGhlIGxvd2VzdCBlcnJvciByYXRlIDE0Ljc5JQ0KDQojIyMjIGdpbmkgaW5kZXgNCg0KR2luaSBJbmRleCBpcyBhIGNyaXRlcmlvbiB1c2VkIGluIHRoZSBDQVJUIChDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmVlcykgYWxnb3JpdGhtIGZvciBhdHRyaWJ1dGUgc2VsZWN0aW9uLiBJdCBtZWFzdXJlcyB0aGUgaW1wdXJpdHkgb2YgYSBzZXQgb2YgdHVwbGVzLCB3aXRoIGxvd2VyIHZhbHVlcyBHaW5pIGEoRCkgaW5kaWNhdGluZyBoaWdoZXIgcHVyaXR5LiBUaGUgR2luaSBJbmRleCBpcyBjYWxjdWxhdGVkIGJ5IHN1bW1pbmcgdGhlIHNxdWFyZWQgcHJvYmFiaWxpdGllcyBvZiBlYWNoIGNsYXNzIGxhYmVsIGluIHRoZSBzZXQNCg0KZm9yIG91ciB0cmVlcywNCg0KZ2luaSBpbmRleCBoYWQgZGlmZmVyZW50IHJvb3QgKHBhcmVudCBzYWxhcnkvaG91c2UgYXJlYS9hdmVyYWdlcyBncmFkZXMpIGZvciBlYWNoIHRyZWUNCg0KfCAgICAgICAgICAgICB8IDUwJS01MCUgfCA3MCUtMzAlIHwgODAlLTIwJSB8DQp8LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tfC0tLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgfCA4NS4yMSUgIHwgNzkuNjYlICB8IDgwLjgxJSAgfA0KfCBFcnJvciByYXRlICB8IDE0Ljc5JSAgfCAyMC4zNCUgIHwgMTkuMTklICB8DQp8IFNlbnNpdGl2aXR5IHwgODguMzclICB8IDc4LjAxJSAgfCA3Ni41MyUgIHwNCnwgU3BlY2lmaWNpdHkgfCA4MS45MyUgIHwgODEuMTclICB8IDg1LjAwJSAgfA0KfCBQcmVjaXNpb24gICB8IDgzLjUyJSAgfCA3OS4xNCUgIHwgODMuMzMlICB8DQoNCnRoZSBldmFsdWF0aW9uIG1ldGhvZCB0YWJsZSBzaG93cyB0aGF0IHRoZSBiZXN0IHBhcnRpdGlvbiBmb3IgZ2FpbiByYXRpbyBzaG93cyB0aGF0IDUwJS01MCUgaXMgdGhlIGJlc3QgbW9kZWwgYmFzZWQgb24gYWNjdXJhY3kodHVwbGVzIHRoYXQgYXJlIGNvcnJlY3RseSBjbGFzc2lmaWVkKSB3aXRoIHBlcmNlbnRhZ2VzIDg1LjIxJSBhbmQgU2Vuc2l0aXZpdHkgKFRydWUgcG9zaXRpdmUgcmVjb2duaXRpb24gcmF0ZSkgd2l0aCA4OC4zNyUgYW5kIFByZWNpc2lvbih0dXBsZXMgbGFiZWxlZCBhcyBwb3NpdGl2ZSBhcmUgYWN0dWFsbHkgcG9zaXRpdmUpIHdpdGggcGVyY2VudGFnZXMgODMuNTIlIG92ZXJhbGwgaXQncyB0aGUgYmVzdCBtb2RlbCBmb3IgZ2luaSBpbmRleCB3aXRoIHRoZSBsb3dlc3QgZXJyb3IgcmF0ZSAxNC43OSUNCg0KKipub3RpY2VhYmxlIG5vdGVzKioNCg0KaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBnZW5kZXIgd2FzIG5vdCBpbmNsdWRlZCBpbiA3IG9mIDkgdHJlZXMgd2hpY2gNCg0KcGFyZW50J3Mgc2FsYXJ5IHdhcyB0aGUgcm9vdCBvZiA0IG9mIDkgdHJlZXMNCg0KYXZlcmFnZSBncmFkZXMgd2FzIHRoZSByb290IG9mIDQgb2YgOSB0cmVlcw0KDQpob3VzZSBhcmVhIHdhcyB0aGUgcm9vdCBvbmx5IG9uY2UgMSBvZiA5IHRyZWUNCg0KSW4gc3VtbWFyeSwgdGhlIGFuYWx5c2lzIG9mIHRoZSBkZWNpc2lvbiB0cmVlcyByZXZlYWxzIHRoYXQgZmluYW5jaWFsIGNpcmN1bXN0YW5jZXMsIGFzIGluZGljYXRlZCBieSBwYXJlbnQncyBzYWxhcnksIGFuZCBhY2FkZW1pYyBwZXJmb3JtYW5jZSwgYXMgaW5kaWNhdGVkIGJ5IGF2ZXJhZ2UgZ3JhZGVzLCBhcmUgY3J1Y2lhbCBmYWN0b3JzIGZvciBjb250ZW1wb3JhcnkgdW5pdmVyc2l0aWVzIHdoZW4gcHJlZGljdGluZyBjb2xsZWdlIGVucm9sbG1lbnQuIEluIHRoaXMgcGFydGljdWxhciBkYXRhc2V0LCBnZW5kZXIgaXMgbm90IGNvbnNpZGVyZWQgaGlnaGx5IGltcG9ydGFudCBpbiB0aGUgZGVjaXNpb24tbWFraW5nIHByb2Nlc3MuDQoNCiMjIyMgYmVzdCBtb2RlbA0KDQp0aGUgNTAlIHRyYWluaW5nLCA1MCUgdGVzdGluZyB3YXMgdGhlIGJlc3QgcGFydGl0aW9uIGZvciBhbGwgYXR0cmlidXRlIHNlbGVjdGlvbiBtZWFzdXJlcyB3aXRoIGFjY3VyYWNpZXMgODUuMjElLiBmb3IgYm90aCBnaW5pIGluZGV4IGFuZCBnYWluIHJhdGlvDQoNCkJvdGggYXR0cmlidXRlIHNlbGVjdGlvbiBtZWFzdXJlcyB5aWVsZCB0aGUgc2FtZSBhY2N1cmFjeSBhbmQgZXJyb3IgcmF0ZSwgc28gd2UgbmVlZCB0byBjaGVjayB0aGUgcmVtYWluaW5nIGV2YWx1YXRpb24gbWV0cmljcyB0aGF0IGlzIHByb3ZpZGVkOg0KDQp8ICAgICAgICAgICAgIHwgZ2FpbiByYXRpbyB8IGdpbmkgaW5kZXggfA0KfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCnwgQWNjdXJhY3kgICAgfCA4NS4yMSUgICAgIHwgODUuMjElICAgICB8DQp8IEVycm9yIHJhdGUgIHwgMTQuNzklICAgICB8IDE0Ljc5JSAgICAgfA0KfCBTZW5zaXRpdml0eSB8IDg3LjYwJSAgICAgfCA4OC4zNyUgICAgIHwNCnwgU3BlY2lmaWNpdHkgfCA4Mi43MyUgICAgIHwgODEuOTMlICAgICB8DQp8IFByZWNpc2lvbiAgIHwgODQuMDElICAgICB8IDgzLjUyJSAgICAgfA0KDQpDb21wYXJpbmcgdGhlIHNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eSwgYW5kIHByZWNpc2lvbjoNCg0KXC0gU2Vuc2l0aXZpdHk6IEdpbmkgSW5kZXggaGFzIGEgaGlnaGVyIHNlbnNpdGl2aXR5IHZhbHVlICg4OC4zNyUpIGNvbXBhcmVkIHRvIEdhaW4gUmF0aW8gKDg3LjYwJSkuIEhpZ2hlciBzZW5zaXRpdml0eSBpbmRpY2F0ZXMgYSBiZXR0ZXIgYWJpbGl0eSB0byBjb3JyZWN0bHkgaWRlbnRpZnkgcG9zaXRpdmUgaW5zdGFuY2VzIHNvICoqZ2luaSBpbmRleCBpcyBiZXR0ZXIgcmVnYXJkaW5nIFNlbnNpdGl2aXR5LioqDQoNClwtIFNwZWNpZmljaXR5OiBHaW5pIEluZGV4IGhhcyBhIGxvd2VyIHNwZWNpZmljaXR5IHZhbHVlICg4MS45MyUpIGNvbXBhcmVkIHRvIEdhaW4gUmF0aW8gKDgyLjczJSkuIEhpZ2hlciBzcGVjaWZpY2l0eSBpbmRpY2F0ZXMgYSBiZXR0ZXIgYWJpbGl0eSB0byBjb3JyZWN0bHkgaWRlbnRpZnkgbmVnYXRpdmUgaW5zdGFuY2VzLg0KDQpcLSBQcmVjaXNpb246IEdhaW4gUmF0aW8gaGFzIGEgaGlnaGVyIHByZWNpc2lvbiB2YWx1ZSAoODQuMDElKSBjb21wYXJlZCB0byBHaW5pIEluZGV4ICg4My41MiUpLiBIaWdoZXIgcHJlY2lzaW9uIGluZGljYXRlcyBhIGJldHRlciBhYmlsaXR5IHRvIGNvcnJlY3RseSBjbGFzc2lmeSBwb3NpdGl2ZSBpbnN0YW5jZXMuDQoNCioqc28gZ2FpbiByYXRpbyBpcyBiZXR0ZXIgcmVnYXJkaW5nKiogKipTcGVjaWZpY2l0eSBhbmQgUHJlY2lzaW9uIC4qKg0KDQpCYXNlZCBvbiB0aGVzZSBjb21wYXJpc29ucywgaXQgaXMgZGlmZmljdWx0IHRvIGRldGVybWluZSBkZWZpbml0aXZlbHkgd2hpY2ggYXR0cmlidXRlIHNlbGVjdGlvbiBtZWFzdXJlIGlzIHRoZSBiZXN0LiBUaGUgY2hvaWNlIGJldHdlZW4gR2luaSBJbmRleCBhbmQgR2FpbiBSYXRpbyBtYXkgZGVwZW5kIG9uIHRoZSBzcGVjaWZpYyByZXF1aXJlbWVudHMgYW5kIHByaW9yaXRpZXMgb2YgdGhlIHByb2JsZW0gYnV0IHNpbmNlIHdlIGZvY3VzIG9uIHRoZSB0cnVlIG5lZ2F0aXZlIG1vcmUgdGhhbiB0cnVlIHBvc2l0aXZlIGJlY2F1c2Ugb3VyIGdvYWwgb2YgY2xhc3NpZmljYXRpb24gdG8gZGV0ZXJtaW5lIHdobyB3aWxsIG5vdCBnbyB0byBjb2xsYWdlLCBzbyB3ZSB3aWxsIGNob29zZSB0aGUgbW9kZWwgYmFzZWQgb24gdGhlIFNwZWNpZmljaXR5IGFuZCBQcmVjaXNpb24gKEdhaW4gcmF0aW8pDQoNCm91ciBmaW5hbCB0cmVlIGlzOg0KDQohW10oaW1hZ2VzL1doYXRzQXBwIEltYWdlIDIwMjMtMTEtMzAgYXQgMjAuMjAuMzNfYTM4MWIxMGEtMDIuanBnKQ0KDQojIyBDbHVzdGVyaW5nICoqQW5hbHlzaXM6KioNCg0KSW4gdGhpcyBhbmFseXNpcywgd2UgYXBwbHkgSy1tZWFucyBjbHVzdGVyaW5nIHRvIHRoZSBkYXRhc2V0IHVzaW5nIGRpZmZlcmVudCB2YWx1ZXMgb2YgSy4gSy1tZWFucyBjbHVzdGVyaW5nIGlzIGFuIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBhbGdvcml0aG0gdGhhdCBwYXJ0aXRpb25zIHRoZSBkYXRhIGludG8gSyBjbHVzdGVycyBiYXNlZCBvbiBzaW1pbGFyaXR5LiBXZSB3aWxsIGV4cGxvcmUgdGhyZWUgZGlmZmVyZW50IHZhbHVlcyBvZiBLIGFuZCBldmFsdWF0ZSB0aGUgY2x1c3RlcmluZyByZXN1bHRzIHVzaW5nIHZhcmlvdXMgbWV0cmljcy4NCg0KIyMjIFJlbW92aW5nIHRoZSBjbGFzcyBsYWJlbCBhbmQgcHJlcGFyaW5nIHRoZSBkYXRhc2V0IGZvciBDbHVzdGVyaW5nDQoNCmBgYHtyfQ0KIA0Kb3JpZ2luYWxfZGF0YSA8LSBQcmVwcm9jZXNzZWRfZGF0YXNldA0KDQojIFJlbW92ZSBhbnkgbm9uLW51bWVyaWMgYXR0cmlidXRlcw0KbnVtZXJpY19kYXRhIDwtIG9yaWdpbmFsX2RhdGFbLCBzYXBwbHkob3JpZ2luYWxfZGF0YSwgaXMubnVtZXJpYyldDQoNCiMgUmVtb3ZlIHRoZSBjbGFzcyBsYWJlbCAnd2lsbF9nb190b19jb2xsZWdlJw0KbnVtZXJpY19kYXRhIDwtIG51bWVyaWNfZGF0YVssICEobmFtZXMobnVtZXJpY19kYXRhKSA9PSAnd2lsbF9nb190b19jb2xsZWdlJyldDQpgYGANCg0KTm93LCB0aGUgJ251bWVyaWNfZGF0YXNldCcgZGF0YXNldCBjb250YWlucyBvbmx5IG51bWVyaWMgYXR0cmlidXRlcyB3aXRob3V0IHRoZSBjbGFzcyBsYWJlbCwgd2hpY2ggbWFrZXMgaXQgcmVhZHkgZm9yIHRoZSBjbHVzdGVyaW5nIHByb2Nlc3MuDQoNClxfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF8NCg0KIyMgSz0yDQoNCkluIG91ciBleHBsb3JhdGlvbiBvZiBrLW1lYW5zIGNsdXN0ZXJpbmcsIHdlIHdpbGwgc3RhcnQgYnkgZXhhbWluaW5nIHRoZSBkYXRhc2V0IHdpdGggSz0yLCByZXByZXNlbnRpbmcgYW4gaW5pdGlhbCBhdHRlbXB0IHRvIHBhcnRpdGlvbiB0aGUgZGF0YSBpbnRvIHR3byBkaXN0aW5jdCBjbHVzdGVycy4gVGhpcyBpbml0aWFsIGFuYWx5c2lzIHdpbGwgc2VydmUgYXMgYSBmb3VuZGF0aW9uYWwgc3RlcCBpbiB1bmRlcnN0YW5kaW5nIHRoZSBpbmhlcmVudCBzdHJ1Y3R1cmUgYW5kIHBhdHRlcm5zDQoNCmBgYHtyfQ0KIyBrLW1lYW5zIGNsdXN0ZXJpbmcgc2V0IGEgc2VlZCBmb3IgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHRvIG1ha2UgdGhlIHJlc3VsdHMgcmVwcm9kdWNpYmxlIA0Kc2V0LnNlZWQoODk1MykNCg0KIyBydW4ga21lYW5zIGNsdXN0ZXJpbmcgdG8gZmluZCAyIGNsdXN0ZXJzDQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhudW1lcmljX2RhdGEsIDIpDQoNCiMgdmlzdWFsaXplIGNsdXN0ZXJpbmcNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gbnVtZXJpY19kYXRhKQ0KDQoNCg0KYGBgDQoNCiMjIyMgVGhlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQNCg0KYGBge3J9DQojYXZlcmFnZSBmb3IgZWFjaCBjbHVzdGVyIA0KYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlciwgZGlzdChudW1lcmljX2RhdGEpKSANCg0KI2stbWVhbnMgY2x1c3RlcmluZyB3aXRoIGVzdGltYXRpbmcgayBhbmQgaW5pdGlhbGl6YXRpb25zIA0KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpDQoNCmBgYA0KDQojIyMjIFRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzDQp0b3RhbF93aXRoaW5zcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcw0KY2F0KCJUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoiLCBzdW0odG90YWxfd2l0aGluc3MpLCAiXG4iKQ0KDQp0cnVlX2xhYmVscyA8LSBjKDEsIDEsIDIsIDEsIDIsIDIsIDMsIDMsIDQsIDQpICANCg0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBrbWVhbnMucmVzdWx0JGNsdXN0ZXINCiANCmBgYA0KDQojIyMjIEJDdWJlZCByZWNhbGwgYW5kIHByZWNpc2lvbg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24NCnByZWNpc2lvbiA8LSAwDQpmb3IgKGkgaW4gdW5pcXVlKHRydWVfbGFiZWxzKSkgew0KICBjbHVzdGVyX2luZGljZXMgPC0gd2hpY2godHJ1ZV9sYWJlbHMgPT0gaSkNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbiArIHN1bSgodGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSAqICh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pIC0gMSkpIC8gc3VtKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcHJlY2lzaW9uIDwtIHByZWNpc2lvbiAvIHN1bSh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzKSkNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHJlY2FsbA0KcmVjYWxsIDwtIDANCmZvciAoaiBpbiB1bmlxdWUoY2x1c3Rlcl9hc3NpZ25tZW50cykpIHsNCiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKGNsdXN0ZXJfYXNzaWdubWVudHMgPT0gaikNCiAgcmVjYWxsIDwtIHJlY2FsbCArIHN1bSgodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgKiAodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgLSAxKSkgLyBzdW0odGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcmVjYWxsIDwtIHJlY2FsbCAvIHN1bSh0YWJsZSh0cnVlX2xhYmVscykpDQoNCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpDQpjYXQoIkJDdWJlZCBSZWNhbGw6IiwgcmVjYWxsLCAiXG4iKQ0KDQoNCmBgYA0KDQojIyMgKipDbHVzdGVyIEFuYWx5c2lzIGZvciBLPTI6KioNCg0KLSAgICoqU2lsaG91ZXR0ZSBXaWR0aDoqKiAwLjM2DQoNCiAgICAtICAgSW5kaWNhdGVzIHJlbGF0aXZlbHkgd2VsbC1zZXBhcmF0ZWQgYW5kIGRpc3RpbmN0IGNsdXN0ZXJzLg0KDQotICAgKipXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoqKiA4NjY5LjA1OQ0KDQogICAgLSAgIE1vZGVyYXRlIGNvbXBhY3RuZXNzOyBjbHVzdGVycyBhcmUgbm90IHRvbyB0aWdodC4NCg0KLSAgICoqQkN1YmVkIFByZWNpc2lvbjoqKiAwLjAwMjMNCg0KICAgIC0gICBMb3cgcHJlY2lzaW9uIHN1Z2dlc3RzIHBvdGVudGlhbCBtaXNhc3NpZ25tZW50cy4NCg0KLSAgICoqQkN1YmVkIFJlY2FsbDoqKiAwLjExNjcNCg0KICAgIC0gICBMb3cgcmVjYWxsIGluZGljYXRlcyBpbmNvbXBsZXRlIHJlcHJlc2VudGF0aW9uIG9mIHRydWUgY2x1c3RlcnMuDQoNClNvLCB0aGUgY2x1c3RlcnMgYXJlIG1vZGVyYXRlbHkgY29tcGFjdCB3aXRoIGEgcmVhc29uYWJsZSBzaWxob3VldHRlIHdpZHRoLCBidXQgbG93IHByZWNpc2lvbiBhbmQgcmVjYWxsIHN1Z2dlc3QgcG90ZW50aWFsIG1pc2NsYXNzaWZpY2F0aW9uIGlzc3Vlcy4NCg0KXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cXw0KDQojIyBLPTUNCg0KTW92aW5nIHRvIEs9NSwgb3VyIGV4cGxvcmF0aW9uIGluIGstbWVhbnMgY2x1c3RlcmluZyBhZHZhbmNlcyB0byBhIGNvbmZpZ3VyYXRpb24gd2l0aCBmaXZlIGNsdXN0ZXJzLiBUaGlzIGFkanVzdG1lbnQgYWxsb3dzIGZvciBhIG1vcmUgZGV0YWlsZWQgcGFydGl0aW9uaW5nIG9mIHRoZSBkYXRhc2V0LCBwb3RlbnRpYWxseSByZXZlYWxpbmcgZmluZXIgZ3JhaW5lZCBwYXR0ZXJucyBhbmQgZGlzdGluY3Rpb25zIGFtb25nIHRoZSBkYXRhIHBvaW50cy4gVGhlIGFuYWx5c2lzIGF0IEs9NSB3aWxsIG9mZmVyIGluc2lnaHRzIGludG8gdGhlIG5hdHVyZSBvZiB0aGVzZSBjbHVzdGVycywgYXNzZXNzaW5nIHRoZWlyIGNoYXJhY3RlcmlzdGljcywgY29tcGFjdG5lc3MsIGFuZCBzZXBhcmF0aW9uLCBhcyB3ZSBzdHJpdmUgdG8gb3B0aW1pemUgdGhlIGNsdXN0ZXJpbmcgY29uZmlndXJhdGlvbiBmb3IgdGhlIHNwZWNpZmljIG51YW5jZXMgb2Ygb3VyIGRhdGFzZXQuDQoNCmBgYHtyfQ0KIyBrLW1lYW5zIGNsdXN0ZXJpbmcgc2V0IGEgc2VlZCBmb3IgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHRvIG1ha2UgdGhlIHJlc3VsdHMgcmVwcm9kdWNpYmxlIA0Kc2V0LnNlZWQoODk1MykNCg0KIyBydW4ga21lYW5zIGNsdXN0ZXJpbmcgdG8gZmluZCA0IGNsdXN0ZXJzDQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhudW1lcmljX2RhdGEsIDUpDQoNCiMgdmlzdWFsaXplIGNsdXN0ZXJpbmcNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gbnVtZXJpY19kYXRhKQ0KDQoNCg0KYGBgDQoNCiMjIyMgVGhlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQNCg0KYGBge3J9DQojYXZlcmFnZSBmb3IgZWFjaCBjbHVzdGVyIA0KYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlciwgZGlzdChudW1lcmljX2RhdGEpKSANCg0KI2stbWVhbnMgY2x1c3RlcmluZyB3aXRoIGVzdGltYXRpbmcgayBhbmQgaW5pdGlhbGl6YXRpb25zIA0KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpDQoNCmBgYA0KDQojIyMjIFRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzDQp0b3RhbF93aXRoaW5zcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcw0KY2F0KCJUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoiLCBzdW0odG90YWxfd2l0aGluc3MpLCAiXG4iKQ0KDQp0cnVlX2xhYmVscyA8LSBjKDEsIDEsIDIsIDEsIDIsIDIsIDMsIDMsIDQsIDQpICANCg0KY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBrbWVhbnMucmVzdWx0JGNsdXN0ZXINCiANCmBgYA0KDQojIyMjIEJDdWJlZCByZWNhbGwgYW5kIHByZWNpc2lvbg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24NCnByZWNpc2lvbiA8LSAwDQpmb3IgKGkgaW4gdW5pcXVlKHRydWVfbGFiZWxzKSkgew0KICBjbHVzdGVyX2luZGljZXMgPC0gd2hpY2godHJ1ZV9sYWJlbHMgPT0gaSkNCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbiArIHN1bSgodGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSAqICh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pIC0gMSkpIC8gc3VtKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcHJlY2lzaW9uIDwtIHByZWNpc2lvbiAvIHN1bSh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzKSkNCg0KIyBDYWxjdWxhdGUgQkN1YmVkIHJlY2FsbA0KcmVjYWxsIDwtIDANCmZvciAoaiBpbiB1bmlxdWUoY2x1c3Rlcl9hc3NpZ25tZW50cykpIHsNCiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKGNsdXN0ZXJfYXNzaWdubWVudHMgPT0gaikNCiAgcmVjYWxsIDwtIHJlY2FsbCArIHN1bSgodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgKiAodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgLSAxKSkgLyBzdW0odGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQ0KfQ0KcmVjYWxsIDwtIHJlY2FsbCAvIHN1bSh0YWJsZSh0cnVlX2xhYmVscykpDQoNCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpDQpjYXQoIkJDdWJlZCBSZWNhbGw6IiwgcmVjYWxsLCAiXG4iKQ0KDQoNCmBgYA0KDQojIyMgKipDbHVzdGVyIEFuYWx5c2lzIGZvciBLPTU6KioNCg0KLSAgICoqU2lsaG91ZXR0ZSBXaWR0aDoqKiAwLjI5DQoNCiAgICAtICAgSW5kaWNhdGVzIGxlc3Mgd2VsbC1zZXBhcmF0ZWQgY2x1c3RlcnMgY29tcGFyZWQgdG8gaz0yDQoNCi0gICAqKldpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzOioqIDQzMDMuODQxDQoNCiAgICAtICAgTG93ZXIgd2l0aGluLWNsdXN0ZXIgc3VtIHN1Z2dlc3RzIG1vcmUgY29tcGFjdCBjbHVzdGVycyB0aGFuIGs9Mg0KDQotICAgKipCQ3ViZWQgUHJlY2lzaW9uOioqIDAuMDAxNjYNCg0KICAgIC0gICBMb3cgcHJlY2lzaW9uIGltcGxpZXMgcG90ZW50aWFsIG1pc2NsYXNzaWZpY2F0aW9ucy4NCg0KLSAgICoqQkN1YmVkIFJlY2FsbDoqKiAwLjEzMzMNCg0KICAgIC0gICBTbGlnaHRseSBoaWdoZXIgcmVjYWxsIGNvbXBhcmVkIHRvIGs9MiwgYnV0IHN0aWxsIGxvdy4NCg0KKipGaW5hbCBBbmFseXNpczoqKiBDbHVzdGVycyBhcmUgbW9yZSBjb21wYWN0IHRoYW4gaz0yIGJ1dCBsZXNzIHdlbGwtc2VwYXJhdGVkLiBQcmVjaXNpb24gYW5kIHJlY2FsbCByZW1haW4gbG93LCBpbmRpY2F0aW5nIHBvdGVudGlhbCBtaXNjbGFzc2lmaWNhdGlvbnMuDQoNClxfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF8NCg0KIyMgSz03DQoNClByb2NlZWRpbmcgdG8gaz03LCBvdXIgaW52ZXN0aWdhdGlvbiBpbiBrLW1lYW5zIGNsdXN0ZXJpbmcgZXhwYW5kcyBmdXJ0aGVyIGFzIHdlIGV4cGxvcmUgYSBjb25maWd1cmF0aW9uIHdpdGggc2V2ZW4gY2x1c3RlcnMuIFRoaXMgYWRqdXN0bWVudCBhaW1zIHRvIGNhcHR1cmUgZXZlbiBtb3JlIG51YW5jZWQgcGF0dGVybnMgYW5kIHZhcmlhdGlvbnMgd2l0aGluIHRoZSBkYXRhc2V0LiBBbmFseXppbmcgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgc2V2ZW4gY2x1c3RlcnMgd2lsbCBwcm92aWRlIGEgbW9yZSBncmFudWxhciB1bmRlcnN0YW5kaW5nIG9mIHRoZSB1bmRlcmx5aW5nIHN0cnVjdHVyZSwgcG90ZW50aWFsbHkgcmV2ZWFsaW5nIHN1YnRsZXRpZXMgdGhhdCBtaWdodCBub3QgYmUgYXMgZXZpZGVudCB3aXRoIGZld2VyIGNsdXN0ZXJzLiBBcyB3ZSBkZWx2ZSBpbnRvIGs9Nywgb3VyIG9iamVjdGl2ZSBpcyB0byByZWZpbmUgb3VyIGNsdXN0ZXJpbmcgY29uZmlndXJhdGlvbiB0byBhbGlnbiBtb3JlIGNsb3NlbHkgd2l0aCB0aGUgaW50cmljYXRlIGRldGFpbHMgcHJlc2VudCBpbiB0aGUgZGF0YXNldC4NCg0KYGBge3J9DQojIGstbWVhbnMgY2x1c3RlcmluZyBzZXQgYSBzZWVkIGZvciByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gdG8gbWFrZSB0aGUgcmVzdWx0cyByZXByb2R1Y2libGUgDQpzZXQuc2VlZCg4OTUzKQ0KDQojIHJ1biBrbWVhbnMgY2x1c3RlcmluZyB0byBmaW5kIDIgY2x1c3RlcnMNCmttZWFucy5yZXN1bHQgPC0ga21lYW5zKG51bWVyaWNfZGF0YSwgNykNCg0KIyB2aXN1YWxpemUgY2x1c3RlcmluZw0KbGlicmFyeShmYWN0b2V4dHJhKQ0KZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBudW1lcmljX2RhdGEpDQoNCg0KDQoNCmBgYA0KDQojIyMjIFRoZSBTaWxob3VldHRlIGNvZWZmaWNpZW50DQoNCmBgYHtyfQ0KI2F2ZXJhZ2UgZm9yIGVhY2ggY2x1c3RlciANCmF2Z19zaWwgPC0gc2lsaG91ZXR0ZShrbWVhbnMucmVzdWx0JGNsdXN0ZXIsIGRpc3QobnVtZXJpY19kYXRhKSkgDQoNCiNrLW1lYW5zIGNsdXN0ZXJpbmcgd2l0aCBlc3RpbWF0aW5nIGsgYW5kIGluaXRpYWxpemF0aW9ucyANCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKQ0KDQpgYGANCg0KIyMjIyBUaGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcw0KdG90YWxfd2l0aGluc3MgPC0ga21lYW5zLnJlc3VsdCR0b3Qud2l0aGluc3MNCmNhdCgiVG90YWwgV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZXM6Iiwgc3VtKHRvdGFsX3dpdGhpbnNzKSwgIlxuIikNCg0KdHJ1ZV9sYWJlbHMgPC0gYygxLCAxLCAyLCAxLCAyLCAyLCAzLCAzLCA0LCA0KSAgDQoNCmNsdXN0ZXJfYXNzaWdubWVudHMgPC0ga21lYW5zLnJlc3VsdCRjbHVzdGVyDQogDQpgYGANCg0KIyMjIyBCQ3ViZWQgcmVjYWxsIGFuZCBwcmVjaXNpb24NCg0KYGBge3J9DQojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uDQpwcmVjaXNpb24gPC0gMA0KZm9yIChpIGluIHVuaXF1ZSh0cnVlX2xhYmVscykpIHsNCiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKHRydWVfbGFiZWxzID09IGkpDQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb24gKyBzdW0oKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkgKiAodGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSAtIDEpKSAvIHN1bSh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pKSkNCn0NCnByZWNpc2lvbiA8LSBwcmVjaXNpb24gLyBzdW0odGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50cykpDQoNCiMgQ2FsY3VsYXRlIEJDdWJlZCByZWNhbGwNCnJlY2FsbCA8LSAwDQpmb3IgKGogaW4gdW5pcXVlKGNsdXN0ZXJfYXNzaWdubWVudHMpKSB7DQogIGNsdXN0ZXJfaW5kaWNlcyA8LSB3aGljaChjbHVzdGVyX2Fzc2lnbm1lbnRzID09IGopDQogIHJlY2FsbCA8LSByZWNhbGwgKyBzdW0oKHRhYmxlKHRydWVfbGFiZWxzW2NsdXN0ZXJfaW5kaWNlc10pICogKHRhYmxlKHRydWVfbGFiZWxzW2NsdXN0ZXJfaW5kaWNlc10pIC0gMSkpIC8gc3VtKHRhYmxlKHRydWVfbGFiZWxzW2NsdXN0ZXJfaW5kaWNlc10pKSkNCn0NCnJlY2FsbCA8LSByZWNhbGwgLyBzdW0odGFibGUodHJ1ZV9sYWJlbHMpKQ0KDQpjYXQoIkJDdWJlZCBQcmVjaXNpb246IiwgcHJlY2lzaW9uLCAiXG4iKQ0KY2F0KCJCQ3ViZWQgUmVjYWxsOiIsIHJlY2FsbCwgIlxuIikNCg0KDQpgYGANCg0KIyMjICoqQ2x1c3RlciBBbmFseXNpcyBmb3IqKiBrPTc6DQoNCi0gICAqKlNpbGhvdWV0dGUgV2lkdGg6KiogMC4yNw0KDQogICAgLSAgIEluZGljYXRlcyBjbHVzdGVycyBhcmUgbGVzcyB3ZWxsLXNlcGFyYXRlZCBjb21wYXJlZCB0byBrPTIgYW5kIGs9NQ0KDQotICAgKipXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoqKiAzMzg5LjY1Ng0KDQogICAgLSAgIExvd2VyIHdpdGhpbi1jbHVzdGVyIHN1bSBzdWdnZXN0cyBtb3JlIGNvbXBhY3QgY2x1c3RlcnMgdGhhbiBrPTUNCg0KLSAgICoqQkN1YmVkIFByZWNpc2lvbjoqKiAwLjAwMDY3DQoNCiAgICAtICAgVmVyeSBsb3cgcHJlY2lzaW9uIGluZGljYXRpbmcgc2lnbmlmaWNhbnQgbWlzY2xhc3NpZmljYXRpb25zLg0KDQotICAgKipCQ3ViZWQgUmVjYWxsOioqIDAuMDY3DQoNCiAgICAtICAgTG93IHJlY2FsbCBzdWdnZXN0aW5nIGluY29tcGxldGUgcmVwcmVzZW50YXRpb24gb2YgdHJ1ZSBjbHVzdGVycy4NCg0KKipGaW5hbCBBbmFseXNpczoqKiBDbHVzdGVycyBhcmUgbGVzcyB3ZWxsLXNlcGFyYXRlZCB0aGFuIGs9MiBhbmQgaz01IGFuZCB3aGlsZSBtb3JlIGNvbXBhY3QgdGhhbiBrPTUsIHByZWNpc2lvbiBhbmQgcmVjYWxsIHJlbWFpbiBsb3csIGluZGljYXRpbmcgcG90ZW50aWFsIG1pc2NsYXNzaWZpY2F0aW9ucy4NCg0KXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cXw0KDQojIyMgKipFbGJvdyBNZXRob2QgSW5zaWdodDoqKg0KDQpXZSBpbXBsZW1lbnRlZCB0aGUgRWxib3cgTWV0aG9kIGZvciBkZXRlcm1pbmluZyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgKGspIGluIGEgay1tZWFucyBjbHVzdGVyaW5nIGFsZ29yaXRobS4NCg0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcyAod3NzKQ0Kd3NzIDwtIGZ1bmN0aW9uKGspIHsNCiAga21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMobnVtZXJpY19kYXRhLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMTApICAjIFlvdSBjYW4gYWRqdXN0IG5zdGFydCBiYXNlZCBvbiB5b3VyIHByZWZlcmVuY2UNCiAgcmV0dXJuKHN1bShrbWVhbnNfcmVzdWx0JHRvdC53aXRoaW5zcykpDQp9DQoNCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcyBmb3IgZGlmZmVyZW50IHZhbHVlcyBvZiBrDQprX3ZhbHVlcyA8LSAxOjEwICAjIFlvdSBjYW4gYWRqdXN0IHRoZSByYW5nZSBvZiBrIHZhbHVlcw0Kd3NzX3ZhbHVlcyA8LSBzYXBwbHkoa192YWx1ZXMsIHdzcykNCg0KIyBQbG90IHRoZSBlbGJvdyBjdXJ2ZQ0KcGxvdChrX3ZhbHVlcywgd3NzX3ZhbHVlcywgdHlwZSA9ICJiIiwgcGNoID0gMTksIGZyYW1lID0gRkFMU0UsIA0KICAgICB4bGFiID0gIk51bWJlciBvZiBDbHVzdGVycyAoaykiLCB5bGFiID0gIlRvdGFsIFdpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzIChXU1MpIiwNCiAgICAgbWFpbiA9ICJFbGJvdyBNZXRob2QiKQ0KDQoNCmBgYA0KDQpUaGUgZWxib3cgbWV0aG9kIGhhcyBpbmRpY2F0ZWQgdGhhdCBLPTUgaXMgYSByZWFzb25hYmxlIGNob2ljZSBpbiB0ZXJtcyBvZiBiYWxhbmNpbmcgdGhlIHRyYWRlLW9mZiBiZXR3ZWVuIGNhcHR1cmluZyB2YXJpYW5jZSBhbmQgbm90IG92ZXJseSBjb21wbGljYXRpbmcgdGhlIG1vZGVsIHdpdGggdG9vIG1hbnkgY2x1c3RlcnMuDQoNCiMjIEZpbmRpbmdzDQoNCiMjIyBDbGFzc2lmaWNhdGlvbjoNCg0KfCAgICAgICAgICAgICB8ICAgICAgICB8IDUwJS01MCUgfCAgICAgICAgfCAgICAgICAgIHwgNzAlLTMwJSB8ICAgICAgICB8ICAgICAgICAgfCA4MCUtMjAlIHwgICAgICAgIHwNCnwtLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tfC0tLS0tLS0tfC0tLS0tLS0tfC0tLS0tLS0tfC0tLS0tLS0tfC0tLS0tLS0tfC0tLS0tLS0tfC0tLS0tLS0tfA0KfCAgICAgICAgICAgICB8IGlnICAgICB8IGlnciAgICAgfCBnaW5pICAgfCBpZyAgICAgIHwgaWdyICAgICB8IGdpbmkgICB8IGlnICAgICAgfCBpZ3IgICAgIHwgZ2luaSAgIHwNCnwgQWNjdXJhY3kgICAgfCA4NC4yMiUgfCA4NS4yMSUgIHwgODUuMjElIHwgODIuMzcgJSB8IDgwLjM0JSAgfCA3OS42NiUgfCA4My44NCAlIHwgODAuMDMlICB8IDgwLjgxJSB8DQp8IEVycm9yIHJhdGUgIHwgMTUuNzglIHwgMTQuNzklICB8IDE0Ljc5JSB8IDE3LjYzJSAgfCAxOS42NiUgIHwgMjAuMzQlIHwgMTYuMTYlICB8IDE5Ljk3JSAgfCAxOS4xOSUgfA0KfCBTZW5zaXRpdml0eSB8IDg5LjUzJSB8IDg3LjYwJSAgfCA4OC4zNyUgfCA3NS44OSUgIHwgNzcuMzAlICB8IDc4LjAxJSB8IDg2LjczJSAgfCA4NC42OSUgIHwgNzYuNTMlIHwNCnwgU3BlY2lmaWNpdHkgfCA3OC43MSUgfCA4Mi43MyUgIHwgODEuOTMlIHwgODguMzElICB8IDgzLjEyJSAgfCA4MS4xNyUgfCA4MS4wMCUgIHwgNzYuMDAlICB8IDg1LjAwJSB8DQp8IFByZWNpc2lvbiAgIHwgODEuMzQlIHwgODQuMDElICB8IDgzLjUyJSB8IDg1LjYwJSAgfCA4MC43NCUgIHwgNzkuMTQlIHwgODEuNzMlICB8IDc3LjU3JSAgfCA4My4zMyUgfA0KDQpBY2N1cmFjeSAocmVjb2duaXRpb24gcmF0ZSk6IFBlcmNlbnRhZ2Ugb2YgdGVzdCBzZXQgdHVwbGVzIHRoYXQgYXJlIGNvcnJlY3RseSBjbGFzc2lmaWVkDQoNCkVycm9yIHJhdGUgKG1pc2NsYXNzaWZpY2F0aW9uIHJhdGUpOiAxIC0tIGFjY3VyYWN5DQoNClNlbnNpdGl2aXR5IChyZWNhbGwpOiBUcnVlIHBvc2l0aXZlIHJlY29nbml0aW9uIHJhdGUNCg0KU3BlY2lmaWNpdHk6IFRydWUgbmVnYXRpdmUgcmVjb2duaXRpb24gcmF0ZQ0KDQpQcmVjaXNpb24gKGV4YWN0bmVzcyk6IFdoYXQgJSBvZiB0dXBsZXMgbGFiZWxlZCBhcyBwb3NpdGl2ZSBhcmUgYWN0dWFsbHkgcG9zaXRpdmUuDQoNCkZvciBwYXJ0aXRpb24gNTAlLTUwJSA6IGdhaW4gcmF0aW8gYW5kIGdpbmkgaW5kZXggZXF1YWxzIGluIGFjY3VyYWN5IDg1LjIxJSBidXQgYXMgd2UgbWVudGlvbiBiZWZvcmUgaW4gW2Jlc3QgbW9kZWxdIHdlIGNob29zZSBiYXNlZCBvbiBQcmVjaXNpb24gODQuMDElIGFuZCBTcGVjaWZpY2l0eSA4Mi43MyUgLCBHYWluIHJhdG8gaXMgdGhlIGJlc3QgbW9kZWwuDQoNCkZvciBwYXJ0aXRpb24gNzAlLTMwJTogaW5mb3JtYXRpb24gZ2FpbiBpcyB0aGUgYmVzdCBtb2RlbCBmb3IgdGhpcyBwYXJ0aXRpb24gd2l0aCBhY2N1cmFjeSA4Mi4zNyAlDQoNCkZvciBwYXJ0aXRpb24gODAlLTIwJTogaW5mb3JtYXRpb24gZ2FpbiBpcyB0aGUgYmVzdCBtb2RlbCBmb3IgdGhpcyBwYXJ0aXRpb24gd2l0aCBhY2N1cmFjeSA4My44NCAlDQoNCm92ZXJhbGwgNTAlLTUwJSB3YXNhIHRoZSBiZXN0IHBhcnRpdGlvbiB3aXRoIGFsZ29yaXRobSBnaW5pIGluZGV4IHdpdGggYWNjdXJhY3kgODUuMjElLg0KDQojIyMgQ2x1c3RlcmluZw0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tKw0KfCBNZXRyaWMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEs9MiAgICAgICAgIHwgSz01ICAgICAgICAgfCBLPTcgICAgICAgIHwNCis9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT0rPT09PT09PT09PT09PSs9PT09PT09PT09PT0rDQp8IEF2ZXJhZ2UgU2lsaG91ZXR0ZSBXaWR0aCAgICAgICAgICAgIHwgMC4zNiAgICAgICAgfCAwLjI5ICAgICAgICB8IDAuMjcgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLSsNCnwgVG90YWwgV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZXMgfCA4NjY5LjA1OSAgICB8IDQzMDMuODQxICAgIHwgMzM4OS42NTYgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tKw0KfCBCQ3ViZWQgUHJlY2lzaW9uICAgICAgICAgICAgICAgICAgICB8IDAuMDAyMzMzMzMzIHwgMC4wMDE2NjY2NjcgfCAwLjAwMDY3ICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0rDQp8IEJDdWJlZCBSZWNhbGwgICAgICAgICAgICAgICAgICAgICAgIHwgMC4xMTY2NjY3ICAgfCAwLjEzMzMzMzMgICB8IDAuMDY3ICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLSsNCg0KSW4gb3VyIG92ZXJhbGwgYW5hbHlzaXMsIHdlIGV2YWx1YXRlZCB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGstbWVhbnMgY2x1c3RlcmluZyBhbGdvcml0aG0gZm9yIHRocmVlIGRpZmZlcmVudCB2YWx1ZXMgb2YgayAoMiwgNSwgYW5kIDcpLiBUaGUga2V5IG1ldHJpY3MgcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIHJlc3VsdGluZyBjbHVzdGVyczoNCg0KMS4gICpBdmVyYWdlIFNpbGhvdWV0dGUgV2lkdGg6Kg0KDQogICAgLSAgIFRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggbWVhc3VyZXMgdGhlIHNlcGFyYXRpb24gYW5kIGNvaGVzaW9uIG9mIGNsdXN0ZXJzLiBBcyBrIGluY3JlYXNlcyBmcm9tIDIgdG8gNywgdGhlcmUgaXMgYSBncmFkdWFsIGRlY2xpbmUgaW4gc2lsaG91ZXR0ZSB3aWR0aCwgaW5kaWNhdGluZyB0aGF0IGNsdXN0ZXJzIGJlY29tZSBsZXNzIGRpc3RpbmN0LiBIb3dldmVyLCBrPTUgc3RpbGwgbWFpbnRhaW5zIGEgcmVhc29uYWJsZSBzaWxob3VldHRlIHdpZHRoLCBzdWdnZXN0aW5nIGEgYmFsYW5jZSBiZXR3ZWVuIHNlcGFyYXRpb24gYW5kIGNvaGVzaW9uLg0KDQoyLiAgKlRvdGFsIFdpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzOioNCg0KICAgIC0gICBUaGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgcmVmbGVjdHMgdGhlIGNvbXBhY3RuZXNzIG9mIGNsdXN0ZXJzLiBOb3RhYmx5LCBrPTcgZXhoaWJpdHMgdGhlIGxvd2VzdCBzdW0gb2Ygc3F1YXJlcywgaW1wbHlpbmcgbW9yZSBjb21wYWN0IGNsdXN0ZXJzIGNvbXBhcmVkIHRvIGs9NSBhbmQgaz0yLiBUaGlzIGFsaWducyB3aXRoIHRoZSBleHBlY3RhdGlvbiB0aGF0IGEgaGlnaGVyIGsgdGVuZHMgdG8geWllbGQgbW9yZSBjb21wYWN0IGNsdXN0ZXJzLg0KDQozLiAgKkJDdWJlZCBQcmVjaXNpb24gYW5kIFJlY2FsbDoqDQoNCiAgICAtICAgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsIG1ldHJpY3MgYXNzZXNzIHRoZSBhY2N1cmFjeSBhbmQgY29tcGxldGVuZXNzIG9mIGNsdXN0ZXJpbmcuIFByZWNpc2lvbiBhbmQgcmVjYWxsIHZhbHVlcyBhcmUgcmVsYXRpdmVseSBsb3cgYWNyb3NzIGFsbCBrIHZhbHVlcywgaW5kaWNhdGluZyBwb3RlbnRpYWwgbWlzY2xhc3NpZmljYXRpb25zLiBIb3dldmVyLCBrPTUgc3RhbmRzIG91dCB3aXRoIHNsaWdodGx5IGhpZ2hlciBwcmVjaXNpb24gYW5kIHJlY2FsbCBjb21wYXJlZCB0byBrPTIgYW5kIGs9Nywgc3VnZ2VzdGluZyBhIGJldHRlciBiYWxhbmNlIGJldHdlZW4gYWNjdXJhY3kgYW5kIGNvbXBsZXRlbmVzcy4NCg0KQ29uc2lkZXJpbmcgdGhlIHJlc3VsdHMgb2J0YWluZWQgZnJvbSB0aGUgZWxib3cgbWV0aG9kLCB3aGljaCBpZGVudGlmaWVzIGs9NSBhcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMsIG91ciBhbmFseXNpcyBhbGlnbnMgd2l0aCB0aGlzIHJlY29tbWVuZGF0aW9uLiBUaGUgZGV0YWlsZWQgZXhhbWluYXRpb24gb2Ygc2lsaG91ZXR0ZSB3aWR0aCwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMsIGFuZCBCQ3ViZWQgbWV0cmljcyByZWluZm9yY2VzIHRoZSBjaG9pY2Ugb2Ygaz01IGFzIGEgc3VpdGFibGUgY29uZmlndXJhdGlvbiwgc3RyaWtpbmcgYSBiYWxhbmNlIGJldHdlZW4gY2x1c3RlciBkaXN0aW5jdGl2ZW5lc3MgYW5kIGludGVybmFsIGNvaGVzaW9uLiBUaGlzIGNvbXByZWhlbnNpdmUgZXZhbHVhdGlvbiBzdXBwb3J0cyB0aGUgZGVjaXNpb24gdG8gcHJvY2VlZCB3aXRoIGs9NSBmb3IgYSBtb3JlIHJlZmluZWQgYW5kIGVmZmVjdGl2ZSBjbHVzdGVyaW5nIHNvbHV0aW9uLg0KDQpJbiB0aGUgZm9sbG93aW5nIGZpZ3VyZXMsIHRoZSB2aXN1YWxpc2F0aW9uIG9mIHRoZSBjbHVzdGVycyBmb3IgZWFjaCB0cmlhbDoNCg0KSz0yOg0KDQohW10oaW1hZ2VzL1doYXRzQXBwIEltYWdlIDIwMjMtMTItMDIgYXQgMjEuMzcuMzNfZDZlNmI1OGEuanBnKQ0KDQprPTUNCg0KIVtdKGltYWdlcy9XaGF0c0FwcCBJbWFnZSAyMDIzLTEyLTAyIGF0IDIxLjM5LjE0Xzc1ZGI5MzQxLmpwZykNCg0KSz03DQoNCiFbXShpbWFnZXMvV2hhdHNBcHAgSW1hZ2UgMjAyMy0xMi0wMiBhdCAyMS4zOS40MF9kNTYzZDY0Yy5qcGcpDQoNCiMjIyANCg0KIyMjIENsYXNzaWZpY2F0aW9uIHZzIENsdXN0ZXJpbmcNCg0KY2xhc3NpZmljYXRpb24gb2ZmZXJzIGEgbW9yZSBpbnRlcnByZXRhYmxlIGFuZCBhY3Rpb25hYmxlIHNvbHV0aW9uIGNvbXBhcmVkIHRvIGNsdXN0ZXJpbmcsIGFzIGl0IGFzc2lnbnMgY2xlYXIgbGFiZWxzIHRvIGRhdGEgcG9pbnRzLiBJbiBhZGRpdGlvbiwgdGhlIGF2YWlsYWJpbGl0eSBvZiBjbGFzcyBsYWJlbHMgZW5oYW5jZXMgdGhlIG1vZGVsJ3MgYWJpbGl0eSB0byBnZW5lcmFsaXplIGFuZCBtYWtlIGluZm9ybWVkIHByZWRpY3Rpb25zLiBXaGlsZSBjbHVzdGVyaW5nIHN0cnVnZ2xlIHdpdGggb3ZlcmxhcHBpbmcgY2x1c3RlcnMgYW5kIGxhY2sgb2YgY2xlYXIgZXZhbHVhdGlvbiBtZXRyaWNzLCBjbGFzc2lmaWNhdGlvbiBsZXZlcmFnZXMgdGhlIGxhYmVsZWQgZGF0YSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZSBhbmQgcHJvdmlkZSBtZWFuaW5nZnVsIGluc2lnaHRzLiBUaGVyZWZvcmUsIGZvciBvdXIgZGF0YXNldCwgdGhlIHV0aWxpemF0aW9uIG9mIGNsYXNzaWZpY2F0aW9uIGlzIG5vdCBvbmx5IGp1c3RpZmllZCBieSBpdHMgaGlnaCBhY2N1cmFjeSBidXQgYWxzbyBieSBpdHMgYWJpbGl0eSB0byBhZGRyZXNzIHRoZSBzcGVjaWZpYyBjaGFyYWN0ZXJpc3RpY3MgYW5kIGNoYWxsZW5nZXMgcHJlc2VudGVkIGluIHRoZSBkYXRhLg0K